﻿/* 
* Copyright (c) 2010, The University of Texas at Austin
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, 
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, 
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, 
* this list of conditions and the following disclaimer in the documentation and/or other materials 
* provided with the distribution.
*
* Neither the name of The University of Texas at Austin nor the names of its contributors may be 
* used to endorse or promote products derived from this software without specific prior written 
* permission.
* 
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR 
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS 
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Bio.Data;
using Bio.Data.Interfaces;
using Bio.Data.Providers.Interfaces;
using Bio.SharedResources;
using Bio.SharedResources.Interfaces;
using Bio.Views.Structure.Editor.Internal.Algorithms;
using Bio.Views.Structure.Editor.Internal.Data;
using Bio.Views.Structure.Editor.Internal.Editing;
using Bio.Views.Structure.Editor.Internal.Rendering;
using Bio.Views.Structure.Editor.Internal.Rendering.Interfaces;
using Bio.Views.Structure.Editor.Internal.Selection;
using Bio.Views.Structure.Editor.Linking;
using Bio.Views.Structure.Editor.Properties;
using Bio.Views.Structure.Editor.Views;
using Bio.Views.ViewModels;
using JulMar.Windows.Interfaces;
using JulMar.Windows.Mvvm;
using Microsoft.Win32;

namespace Bio.Views.Structure.Editor.ViewModels
{
    /// <summary>
    /// This ViewModel would undergo signficant modifications in any refactoring.
    /// </summary>
    public class SecondaryStructureEditorViewModel : BioViewModel<SecondaryStructureEditor>, IStructureElementVisualComponent
    {
        private static readonly FontFamilyConverter fontFamilyChecker = new FontFamilyConverter();
        private static readonly FontWeightConverter fontWeightChecker = new FontWeightConverter();
        private static readonly FontStyleConverter fontStyleChecker = new FontStyleConverter();
        private static readonly ColorConverter colorChecker = new ColorConverter();

        private Dictionary<string, StructureModelData> _loadedStructures;
        private MenuItem _synchronizeViewportItem;
        private NucleotideVisualRenderingOptionsDataCollection _runtimeNucleotideVisualRenderingOptions;
        private NucleotideVisualRenderingOptionsDataCollection _defaultNucleotideVisualRenderingOptions;
        private BasePairVisualRenderingOptionsDataCollection _runtimeBasePairVisualRenderingOptions;
        private BasePairVisualRenderingOptionsDataCollection _defaultBasePairVisualRenderingOptions;

        private LinkedSecondaryStructureEditorProxy _commLinkProxy;
        private ILinkableAlignmentView _linkedAlignment;

        //Nucleotide and Base Pair Rendering Properties applied to the entire Structure Drawing. The fact that any of this is here is basically a Kluge for now, can be fixed in a refactoring.
        private FontStyle? _nucleotideFontStyle;
        private FontFamily _nucleotideFontFamily;
        private FontWeight? _nucleotideFontWeight;
        private double? _nucleotideFontSize;
        private Brush _nucleotideFill;
        private char? _nucleotideSubstitution;
        private bool? _isNucleotideSubstituted;
        private double? _nucleotideCircleDiameter;
        private double? _nucleotideCircleEdgeThickness;
        private bool? _nucleotideCircleIsFilled;
        private Brush _nucleotideCircleFill;
        private Brush _nucleotideCircleStroke;
        private NucleotideRenderingMode? _nucleotideRenderingStyle;
        private double? _basePairConnectorSize;
        private double? _basePairConnectorStrokeThickness;
        private bool? _basePairConnectorIsFilled;
        private bool? _basePairConnectorIsVisible;
        private BasePairConnectorRenderingMode? _basePairConnectorRenderingStyle;
        private Brush _basePairConnectorFill;
        private Brush _basePairConnectorStroke;

        //These events should all "go away" on refactoring when the viewmodel executes these operations directly and all the IStructureElementVisualComponents are ViewModels managed from here.
        //For now, we have this crazy hack of custom events and triggers.
        #region Events

        //public event EventHandler UndoAll;

        public event EventHandler UndoLastEdit;

        public event EventHandler SynchronizeStructureModelState;

        public event EventHandler NotifyStructureDrawingVisualEdited;

        public event EventHandler DeleteSelectedBasePair;

        public event EventHandler DeleteSelectedHelix;

        public event EventHandler DeleteBasePairsFromRange;

        public event EventHandler CreateStructureElement;

        public event EventHandler SnapSelectedHelixToOpeningBasePair;

        public event EventHandler SnapSelectedHelixToClosingBasePair;

        public event EventHandler ResizeStructureElement;

        public event EventHandler TranslateSelectedStructureElement;

        public event EventHandler RotateSelectedStructureElement;

        public event EventHandler EditStructureElementProperties;

        public event EventHandler StructureModelDataChanged;

        public event EventHandler SelectStructureElementContainingNucleotide;

        #endregion

        #region IStructureElementVisualComponent Members

        public bool IsNucleotideFillInheriting()
        {
            return (NucleotideFill == null) ? true : false;
        }

        public bool IsNucleotideCircleFillInheriting()
        {
            return (NucleotideCircleFill == null) ? true : false;
        }

        public bool IsNucleotideCircleStrokeInheriting()
        {
            return (NucleotideCircleStroke == null) ? true : false;
        }

        public bool IsBasePairConnectorFillInheriting()
        {
            return (BasePairConnectorFill == null) ? true : false;
        }

        public bool IsBasePairConnectorStrokeInheriting()
        {
            return (BasePairConnectorStroke == null) ? true : false;
        }

        public bool IsComponentVisible
        {
            get { return true; }
            set { }
        }

        public IStructureElementVisualComponent ParentComponent
        {
            get { return null; }
            set { }
        }

        public IEnumerable<int> NucleotideIndices
        {
            get { return Enumerable.Empty<int>(); }
        }

        public Visual VisualComponent
        {
            get { throw new NotImplementedException(); }
        }

        public IEnumerable<IStructureElementVisualComponent> Children
        {
            get { return Enumerable.Empty<IStructureElementVisualComponent>(); }
        }

        public void PropagateVisualPropertyChanged()
        {
        }

        public void AddChild(IStructureElementVisualComponent component)
        {
        }

        public void RemoveChild(IStructureElementVisualComponent component)
        {
        }

        public void NotifyParentHierarchyChanged()
        {
        }

        public bool HitTest(Point point, bool includeChildren)
        {
            throw new NotImplementedException();
        }

        public void MakeSelectable()
        {
        }

        public void MakeUnselectable()
        {
        }

        public void Select(bool showNucleotideConnectors)
        {
        }

        public void Deselect()
        {
        }

        public void ContextuallySelect()
        {
        }

        public void ContextuallyDeselect()
        {
        }

        public Visual MakeVisualSnapshot()
        {
            throw new NotImplementedException();
        }

        public void StartEditTracking()
        {
            if (PropertyContextManager == null)
            {
                PropertyContextManager = new SecondaryStructureDrawingPropertyContext(this);
            }
        }

        public void ApplyPropertyEditSet(StructureElementPropertyChangeSet set, bool interruptEditTracking)
        {
            throw new NotImplementedException();
        }

        public void InterruptEditTracking()
        {
            PauseEditTracking = true;
        }

        public void UninterruptEditTracking()
        {
            PauseEditTracking = false;
        }

        public void InterruptEditNotification()
        {
            PauseEditNotification = true;
        }

        public void UninterruptEditNotification()
        {
            PauseEditNotification = false;
        }

        public void UndoAllPropertyChanges()
        {
            throw new NotImplementedException();
        }

        public void UndoLastPropertyChange()
        {
            if (PropertyContextManager != null)
            {
                PropertyContextManager.UndoLast();
                InterruptEditTracking();
                PropertyContextManager.Apply(); //We tell the property context manager to apply the rolled back state to us
                UninterruptEditTracking();
            }
        }

        public object SynchronizeProperties()
        {
            if (PropertyContextManager != null)
            {
                return PropertyContextManager.Save(); //We don't dump the edited state
            }
            return null;
        }

        public object SaveProperties()
        {
            if (PropertyContextManager != null)
            {
                object retValue = PropertyContextManager.Save();
                PropertyContextManager = new SecondaryStructureDrawingPropertyContext(this);
                return retValue;
            }
            return null;
        }

        public bool IsDirty()
        {
            if (PropertyContextManager == null)
                return false;
            return (PropertyContextManager.EditCount() > 0) ? true : false;
        }
      
        public FontFamily NucleotideFontFamily
        {
            get { return _nucleotideFontFamily; }
            set
            {
                if (PropertyContextManager != null && !PauseEditTracking)
                {
                    PropertyContextManager.NucleotideFontFamily = value;
                    RaiseStructureDrawingVisualEdited();
                }
                _nucleotideFontFamily = value;
                OnPropertyChanged("NucleotideFontFamily");
            }
        }

        public FontStyle? NucleotideFontStyle
        {
            get { return _nucleotideFontStyle; }
            set
            {
                if (PropertyContextManager != null && !PauseEditTracking)
                {
                    PropertyContextManager.NucleotideFontStyle = value;
                    RaiseStructureDrawingVisualEdited();
                }
                _nucleotideFontStyle = value;
                OnPropertyChanged("NucleotideFontStyle");
            }
        }

        public FontWeight? NucleotideFontWeight
        {
            get { return _nucleotideFontWeight; }
            set
            {
                if (PropertyContextManager != null && !PauseEditTracking)
                {
                    PropertyContextManager.NucleotideFontWeight = value;
                    RaiseStructureDrawingVisualEdited();
                }
                _nucleotideFontWeight = value;
                OnPropertyChanged("NucleotideFontWeight");
            }
        }

        public double? NucleotideFontSize
        {
            get { return _nucleotideFontSize; }
            set
            {
                if (PropertyContextManager != null && !PauseEditTracking)
                {
                    PropertyContextManager.NucleotideFontSize = value;
                    RaiseStructureDrawingVisualEdited();
                }
                _nucleotideFontSize = value;
                OnPropertyChanged("NucleotideFontSize");
            }
        }

        public NucleotideRenderingMode? NucleotideRenderingStyle
        {
            get { return _nucleotideRenderingStyle; }
            set
            {
                if (PropertyContextManager != null && !PauseEditTracking)
                {
                    PropertyContextManager.NucleotideRenderingStyle = value;
                    RaiseStructureDrawingVisualEdited();
                }
                _nucleotideRenderingStyle = value;
                OnPropertyChanged("NucleotideRenderingStyle");
            }
        }

        public Brush NucleotideFill
        {
            get { return _nucleotideFill; }
            set
            {
                if (PropertyContextManager != null && !PauseEditTracking)
                {
                    PropertyContextManager.NucleotideFill = value;
                    RaiseStructureDrawingVisualEdited();
                }
                _nucleotideFill = value;
                OnPropertyChanged("NucleotideFill");
            }
        }

        public bool? IsNucleotideSubstituted
        {
            get { return _isNucleotideSubstituted; }
            set
            {
                if (PropertyContextManager != null && !PauseEditTracking)
                {
                    PropertyContextManager.IsNucleotideSubstituted = value;
                    RaiseStructureDrawingVisualEdited();
                }
                _isNucleotideSubstituted = value;
                OnPropertyChanged("IsNucleotideSubstituted");
            }
        }

        public char? NucleotideSubstitution
        {
            get { return _nucleotideSubstitution; }
            set
            {
                if (PropertyContextManager != null && !PauseEditTracking)
                {
                    PropertyContextManager.NucleotideSubstitution = value;
                    RaiseStructureDrawingVisualEdited();
                }
                _nucleotideSubstitution = value;
                OnPropertyChanged("NucleotideSubstitution");
            }
        }

        public double? NucleotideCircleDiameter
        {
            get { return _nucleotideCircleDiameter; }
            set
            {
                if (PropertyContextManager != null && !PauseEditTracking)
                {
                    PropertyContextManager.NucleotideCircleDiameter = value;
                    RaiseStructureDrawingVisualEdited();
                }
                _nucleotideCircleDiameter = value;
                OnPropertyChanged("NucleotideCircleDiameter");
            }
        }

        public double? NucleotideCircleEdgeThickness
        {
            get { return _nucleotideCircleEdgeThickness; }
            set
            {
                if (PropertyContextManager != null && !PauseEditTracking)
                {
                    PropertyContextManager.NucleotideCircleEdgeThickness = value;
                    RaiseStructureDrawingVisualEdited();
                }
                _nucleotideCircleEdgeThickness = value;
                OnPropertyChanged("NucleotideCircleEdgeThickness");
            }
        }

        public bool? NucleotideCircleIsFilled
        {
            get { return _nucleotideCircleIsFilled; }
            set
            {
                if (PropertyContextManager != null && !PauseEditTracking)
                {
                    PropertyContextManager.NucleotideCircleIsFilled = value;
                    RaiseStructureDrawingVisualEdited();
                }
                _nucleotideCircleIsFilled = value;
                OnPropertyChanged("NucleotideCircleIsFilled");
            }
        }

        public Brush NucleotideCircleFill
        {
            get { return _nucleotideCircleFill; }
            set
            {
                if (PropertyContextManager != null && !PauseEditTracking)
                {
                    PropertyContextManager.NucleotideCircleFill = value;
                    RaiseStructureDrawingVisualEdited();
                }
                _nucleotideCircleFill = value;
                OnPropertyChanged("NucleotideCircleFill");
            }
        }

        public Brush NucleotideCircleStroke
        {
            get { return _nucleotideCircleStroke; }
            set
            {
                if (PropertyContextManager != null && !PauseEditTracking)
                {
                    PropertyContextManager.NucleotideCircleStroke = value;
                    RaiseStructureDrawingVisualEdited();
                }
                _nucleotideCircleStroke = value;
                OnPropertyChanged("NucleotideCircleStroke");
            }
        }

        public bool? BasePairConnectorIsFilled
        {
            get { return _basePairConnectorIsFilled; }
            set
            {
                if (PropertyContextManager != null && !PauseEditTracking)
                {
                    PropertyContextManager.BasePairConnectorIsFilled = value;
                    RaiseStructureDrawingVisualEdited();
                }
                _basePairConnectorIsFilled = value;
                OnPropertyChanged("BasePairConnectorIsFilled");
            }
        }

        public bool? BasePairConnectorIsVisible
        {
            get { return _basePairConnectorIsVisible; }
            set
            {
                if (PropertyContextManager != null && !PauseEditTracking)
                {
                    PropertyContextManager.BasePairConnectorIsVisible = value;
                    RaiseStructureDrawingVisualEdited();
                }
                _basePairConnectorIsVisible = value;
                OnPropertyChanged("BasePairConnectorIsVisible");
            }
        }

        public Brush BasePairConnectorFill
        {
            get { return _basePairConnectorFill; }
            set
            {
                if (PropertyContextManager != null && !PauseEditTracking)
                {
                    PropertyContextManager.BasePairConnectorFill = value;
                    RaiseStructureDrawingVisualEdited();
                }
                _basePairConnectorFill = value;
                OnPropertyChanged("BasePairConnectorFill");
            }
        }

        public Brush BasePairConnectorStroke
        {
            get { return _basePairConnectorStroke; }
            set
            {
                if (PropertyContextManager != null && !PauseEditTracking)
                {
                    PropertyContextManager.BasePairConnectorStroke = value;
                    RaiseStructureDrawingVisualEdited();
                }
                _basePairConnectorStroke = value;
                OnPropertyChanged("BasePairConnectorStroke");
            }
        }

        public double? BasePairConnectorSize
        {
            get { return _basePairConnectorSize; }
            set
            {
                if (PropertyContextManager != null && !PauseEditTracking)
                {
                    PropertyContextManager.BasePairConnectorSize = value;
                    RaiseStructureDrawingVisualEdited();
                }
                _basePairConnectorSize = value;
                OnPropertyChanged("BasePairConnectorSize");
            }
        }

        public double? BasePairConnectorStrokeThickness
        {
            get { return _basePairConnectorStrokeThickness; }
            set
            {
                if (PropertyContextManager != null && !PauseEditTracking)
                {
                    PropertyContextManager.BasePairConnectorStrokeThickness = value;
                    RaiseStructureDrawingVisualEdited();
                }
                _basePairConnectorStrokeThickness = value;
                OnPropertyChanged("BasePairConnectorStrokeThickness");
            }
        }

        public BasePairConnectorRenderingMode? BasePairConnectorRenderingStyle
        {
            get { return _basePairConnectorRenderingStyle; }
            set
            {
                if (PropertyContextManager != null && !PauseEditTracking)
                {
                    PropertyContextManager.BasePairConnectorRenderingStyle = value;
                    RaiseStructureDrawingVisualEdited();
                }
                 _basePairConnectorRenderingStyle = value;
                OnPropertyChanged("BasePairConnectorRenderingStyle");
            }
        }

        #endregion

        public NucleotideVisualRenderingOptionsDataCollection RuntimeNucleotideVisualRenderingOptions
        {
            get { return _runtimeNucleotideVisualRenderingOptions; }
            private set
            {
                _runtimeNucleotideVisualRenderingOptions = value;
                OnPropertyChanged("RuntimeNucleotideVisualRenderingOptions");
            }
        }

        public BasePairVisualRenderingOptionsDataCollection RuntimeBasePairVisualRenderingOptions
        {
            get { return _runtimeBasePairVisualRenderingOptions; }
            private set
            {
                _runtimeBasePairVisualRenderingOptions = value;
                OnPropertyChanged("RuntimeBasePairVisualRenderingOptions");
            }
        }

        private StructureModelData _currentStructure;
        public StructureModelData CurrentStructure
        {
            get { return _currentStructure; }
            private set
            {
                StructureModelData prevStructure = _currentStructure;
                _currentStructure = value;
                LoadCurrentStructureMetadata();
                Title = _currentStructure.Title;
                if(prevStructure != _currentStructure && _commLinkProxy != null)
                    _commLinkProxy.FireVisibleStructureModelChangedEvent();
                OnPropertyChanged("CurrentStructure", "IsCurrentStructureLinked", "Title", "RCADSequenceId", "RCADAlignmentId", "RCADScientificName", "IsStructureModelSetLinkable");
            }
        }

        private BitmapSource _currentDrawingSnapshot;
        public BitmapSource CurrentDrawingSnapshot
        {
            get { return _currentDrawingSnapshot; }
            set
            {
                _currentDrawingSnapshot = value;
                OnPropertyChanged("CurrentDrawingSnapshot");
            }
        }

        private Size _currentCanvasSize;
        public Size CurrentCanvasSize
        {
            get { return _currentCanvasSize; }
            set
            {
                _currentCanvasSize = value;
                OnPropertyChanged("CurrentCanvasSize");
            }
        }

        private Size _currentDrawingSize;
        public Size CurrentDrawingSize
        {
            get { return _currentDrawingSize; }
            set
            {
                _currentDrawingSize = value;
                OnPropertyChanged("CurrentDrawingSize");
            }
        }

        private Point _currentDrawingOffset;
        public Point CurrentDrawingOffset
        {
            get { return _currentDrawingOffset; }
            set
            {
                _currentDrawingOffset = value;
                OnPropertyChanged("CurrentDrawingOffset");
            }
        }

        private Size _currentViewportSize;
        public Size CurrentViewportSize
        {
            get { return _currentViewportSize; }
            set
            {
                _currentViewportSize = value;
                OnPropertyChanged("CurrentViewportSize");
            }
        }

        private Point _currentViewportTopLeft;
        public Point CurrentViewportTopLeft
        {
            get { return _currentViewportTopLeft; }
            set
            {
                //Do a check to prevent a second, repetitive update since this is two-way bound to the view.
                if (!_currentViewportTopLeft.Equals(value))
                {
                    _currentViewportTopLeft = value;
                    OnPropertyChanged("CurrentViewportTopLeft");
                }
            }
        }

        private bool _currentDrawingDirtyState;
        public bool CurrentDrawingDirtyState
        {
            get { return _currentDrawingDirtyState; }
            set
            {
                _currentDrawingDirtyState = value;
                if (_currentDrawingDirtyState)
                    Title = string.Format("{0}*", _currentStructure.Title);
                else
                    Title = _currentStructure.Title;
                OnPropertyChanged("Title");
            }
        }

        /*private double _currentViewportTop;
        public double CurrentViewportTop
        {
            get { return _currentViewportTop; }
            set
            {
                _currentViewportTop = value;
                OnPropertyChanged("CurrentViewportTop");
            }
        }

        private double _currentViewportLeft;
        public double CurrentViewportLeft
        {
            get { return _currentViewportLeft; }
            set
            {
                _currentViewportLeft = value;
                OnPropertyChanged("CurrentViewportLeft");
            }
        }*/

        private int _selectedElementID;
        public int SelectedElementID
        {
            get { return _selectedElementID; }
            set
            {
                _selectedElementID = value;
                OnPropertyChanged("SelectedElementID");
            }
        }

        private StructureElementSelectionMode _elementSelectionMode;
        public StructureElementSelectionMode ElementSelectionMode
        {
            get { return _elementSelectionMode; }
            set
            {
                _elementSelectionMode = value;
                OnPropertyChanged("ElementSelectionMode", "EditMenu", "SelectedElementContextMenu", "SelectedStructureElementNucleotideIndices", "ElementSelectionModeAsStructureElementType");
            }
        }

        private object _selectedElement;
        public object SelectedElement
        {
            get { return _selectedElement; }
            set
            {
                object prevSelectedElement = _selectedElement;
                _selectedElement = value;
                if (_selectedElement != prevSelectedElement && _commLinkProxy != null)
                    _commLinkProxy.FireVisibleStructureModelSelectionChangedEvent();
                OnPropertyChanged("SelectedElement", "SelectedStructureElementNucleotideIndices");
            }
        }

        private bool _isEditable;
        public bool IsEditable
        {
            get { return _isEditable; }
            set
            {
                _isEditable = value;
                OnPropertyChanged("IsEditable");
            }
        }

        private static bool _isBirdsEyeViewShowing;
        public bool IsBirdsEyeViewShowing
        {
            get { return _isBirdsEyeViewShowing; }
            set
            {
                _isBirdsEyeViewShowing = value;
                OnPropertyChanged("IsBirdsEyeViewShowing");
            }
        }

        private static bool _isPropertyInspectorViewShowing;
        public bool IsPropertyInspectorViewShowing
        {
            get { return _isPropertyInspectorViewShowing; }
            set
            {
                _isPropertyInspectorViewShowing = value;
                OnPropertyChanged("IsPropertyInspectorViewShowing");
            }
        }

        public override string Title
        {
            get { return base.Title; }
            set { base.Title = value; }
        }

        private bool _synchronizeViewport;
        public bool SynchronizeViewport
        {
            get { return _synchronizeViewport; }
            private set
            {
                _synchronizeViewport = value;
                if(_synchronizeViewportItem != null)
                    _synchronizeViewportItem.IsChecked = _synchronizeViewport;
                OnPropertyChanged("SynchronizeViewport");
            }
        }

        public bool IsCurrentStructureLinked
        {
            get { return (IsStructureModelSetLinked &&
                          CurrentStructure.SequenceId.Equals(_linkedAlignment.FocusedRowID)) ? true : false; }
        }

        public bool IsStructureModelSetLinked
        {
            get { return (_linkedAlignment != null) ? true : false; }
        }

        /// <summary>
        /// For now, we assume if the current structure is linkable, the set is linkable
        /// </summary>
        public bool IsStructureModelSetLinkable
        {
            get { return (CurrentStructure.IsLinkable) ? true : false; }
        }

        public int InMemoryModelCount
        {
            get
            {
                return (_loadedStructures != null && _loadedStructures.Values.Count <= 0) ? 0 :
                            _loadedStructures.Values.Where(smd => smd.InMemoryDataOnly).Count();
            }
        }

        public int LoadedModelCount
        {
            get { return _loadedStructures.Count; }
        }

        public IList<IStructureModelBioEntity> LoadedModels
        {
            get { return _loadedStructures.Values.Select(smData => smData.Entity).ToList<IStructureModelBioEntity>(); }
        }

        public StructureElementType ElementSelectionModeAsStructureElementType
        {
            get
            {
                switch (ElementSelectionMode)
                {
                    case StructureElementSelectionMode.None:
                        return StructureElementType.None;
                    case StructureElementSelectionMode.Nucleotide:
                        return StructureElementType.Nucleotide;
                    case StructureElementSelectionMode.BasePair:
                        return StructureElementType.BasePair;
                    case StructureElementSelectionMode.Helix:
                        return StructureElementType.Helix;
                    case StructureElementSelectionMode.Strand:
                        return StructureElementType.Strand;
                    case StructureElementSelectionMode.Range:
                    case StructureElementSelectionMode.Domain:
                        return StructureElementType.Range;
                    default:
                        return StructureElementType.None;
                };
            }
        }

        public IEnumerable<int> SelectedStructureElementNucleotideIndices
        {
            get
            {
                if (SelectedElement == null)
                    return Enumerable.Empty<int>();
                else if (ElementSelectionMode == StructureElementSelectionMode.None)
                    return Enumerable.Empty<int>();
                else
                {
                    IStructureElementVisualComponent visual = SelectedElement as IStructureElementVisualComponent;
                    if (visual != null)
                    {
                        return visual.NucleotideIndices;
                    }
                    else { return Enumerable.Empty<int>(); }
                }
            }
        }

        public string RCADScientificName
        {
            get { return CurrentStructure.ScientificName; }
        }

        public string RCADSequenceId
        {
            get { return CurrentStructure.SequenceId; }
        }

        public string RCADAlignmentId
        {
            get { return CurrentStructure.AlignmentId; }
        }

        private MenuItem _nucleotideElementEditMenu;
        public MenuItem NucleotideElementEditMenu
        {
            get
            {
                if (_nucleotideElementEditMenu == null)
                {
                    _nucleotideElementEditMenu = new MenuItem("Nucleotide");
                    MenuItem nucleotidePropertiesEditMenu = new MenuItem("Properties");
                    nucleotidePropertiesEditMenu.Children.Add(new MenuItem("Selected") { Command = ChangeNucleotidePropertiesCommand });
                    nucleotidePropertiesEditMenu.Children.Add(new MenuItem("All with Filter") { Command = ChangeAllNucleotidePropertiesFilterByTypeCommand });
                    nucleotidePropertiesEditMenu.Children.Add(new MenuItem("All") { Command = ChangeAllNucleotidePropertiesCommand });
                    _nucleotideElementEditMenu.Children.Add(nucleotidePropertiesEditMenu);
                    _nucleotideElementEditMenu.Children.Add(TranslateStructureElementMenu);
                }
                return _nucleotideElementEditMenu;
            }
        }

        private MenuItem _basePairElementEditMenu;
        public MenuItem BasePairElementEditMenu
        {
            get
            {
                if (_basePairElementEditMenu == null)
                {
                    _basePairElementEditMenu = new MenuItem("Base Pair");
                    MenuItem basePairPropertiesEditMenu = new MenuItem("Properties");
                    basePairPropertiesEditMenu.Children.Add(new MenuItem("Selected") { Command = ChangeBasePairPropertiesCommand });
                    basePairPropertiesEditMenu.Children.Add(new MenuItem("All with Filter") { Command = ChangeAllBasePairPropertiesFilterByTypeCommand });
                    basePairPropertiesEditMenu.Children.Add(new MenuItem("All") { Command = ChangeAllBasePairsPropertiesCommand });
                    _basePairElementEditMenu.Children.Add(basePairPropertiesEditMenu);
                    _basePairElementEditMenu.Children.Add(TranslateStructureElementMenu);
                    _basePairElementEditMenu.Children.Add(RotateStructureElementMenu);
                    _basePairElementEditMenu.Children.Add(ResizeBasePairMenu);
                }
                return _basePairElementEditMenu;
            }
        }

        private MenuItem _helixElementEditMenu;
        public MenuItem HelixElementEditMenu
        {
            get
            {
                if (_helixElementEditMenu == null)
                {
                    _helixElementEditMenu = new MenuItem("Helix");
                    MenuItem helixSnapEditMenu = new MenuItem("Snap");
                    helixSnapEditMenu.Children.Add(new MenuItem("To Opening Base Pair") { Command = SnapHelixToOpeningBasePairCommand });
                    helixSnapEditMenu.Children.Add(new MenuItem("To Closing Base Pair") { Command = SnapHelixToClosingBasePairCommand });
                    _helixElementEditMenu.Children.Add(helixSnapEditMenu);
                    _helixElementEditMenu.Children.Add(TranslateStructureElementMenu);
                    _helixElementEditMenu.Children.Add(RotateStructureElementMenu);
                    _helixElementEditMenu.Children.Add(ResizeHelixMenu);
                }
                return _helixElementEditMenu;
            }
        }

        private MenuItem _loopElementEditMenu;
        public MenuItem LoopElementEditMenu
        {
            get
            {
                if (_loopElementEditMenu == null)
                {
                    _loopElementEditMenu = new MenuItem("Strand");
                    _loopElementEditMenu.Children.Add(TranslateStructureElementMenu);
                    //_loopElementEditMenu.Children.Add(ResizeLoopMenu); Removed until the snapping code can be fixed.
                }
                return _loopElementEditMenu;
            }
        }

        private MenuItem _rangeEditMenu;
        public MenuItem RangeEditMenu
        {
            get
            {
                if (_rangeEditMenu == null)
                {
                    _rangeEditMenu = new MenuItem("Range");
                    MenuItem rangePropertiesEditMenu = new MenuItem("Properties");
                    rangePropertiesEditMenu.Children.Add(new MenuItem("Selected") { Command = ChangeRangePropertiesCommand });
                    rangePropertiesEditMenu.Children.Add(new MenuItem("Selected with Filter") { Command = ChangeRangePropertiesFilterByNucleotideTypeCommand });
                    _rangeEditMenu.Children.Add(rangePropertiesEditMenu);
                    _rangeEditMenu.Children.Add(TranslateRangeMenu);
                }
                return _rangeEditMenu;
            }
        }

        private MenuItem _domainEditMenu;
        public MenuItem DomainEditMenu
        {
            get
            {
                if (_domainEditMenu == null)
                {
                    _domainEditMenu = new MenuItem("Domain");
                    MenuItem domainPropertiesEditMenu = new MenuItem("Properties");
                    domainPropertiesEditMenu.Children.Add(new MenuItem("Selected") { Command = ChangeRangePropertiesCommand });
                    domainPropertiesEditMenu.Children.Add(new MenuItem("Selected with Filter") { Command = ChangeRangePropertiesFilterByNucleotideTypeCommand });
                    _domainEditMenu.Children.Add(domainPropertiesEditMenu);
                    _domainEditMenu.Children.Add(TranslateRangeMenu);
                }
                return _domainEditMenu;
            }
        }

        private MenuItem _structureElementEditMenu;
        public MenuItem StructureElementsEditMenu
        {
            get
            {
                if (_structureElementEditMenu == null)
                    _structureElementEditMenu = new MenuItem("Structure Element");
                
                _structureElementEditMenu.Children.Add(NucleotideElementEditMenu);
                _structureElementEditMenu.Children.Add(BasePairElementEditMenu);                
                _structureElementEditMenu.Children.Add(HelixElementEditMenu);
                _structureElementEditMenu.Children.Add(LoopElementEditMenu);
                _structureElementEditMenu.Children.Add(RangeEditMenu);
                _structureElementEditMenu.Children.Add(DomainEditMenu);
                return _structureElementEditMenu;
            }
        }

        private MenuItem _exportMenu;
        public MenuItem ExportMenu
        {
            get
            {
                if (_exportMenu == null)
                {
                    _exportMenu = new MenuItem("Export");
                    _exportMenu.Children.Add(new MenuItem("Strml") { Command = ExportDiagramStrmlCommand });
                    _exportMenu.Children.Add(new MenuItem("RNAml") { Command = ExportDiagramRNAmlCommand });
                    _exportMenu.Children.Add(new MenuItem("BPSeq") { Command = ExportDiagramBPSeqCommand });
                    _exportMenu.Children.Add(new MenuItem("XRNA") { Command = ExportDiagramXRNACommand });
                    _exportMenu.Children.Add(new MenuItem("CT") { Command = ExportDiagramCTCommand });
                }
                return _exportMenu;
            }
        }

        private ObservableCollection<MenuItem> _selectedElementContextMenu;
        public ObservableCollection<MenuItem> SelectedElementContextMenu
        {
            get
            {
                if (_selectedElementContextMenu == null)
                {
                    _selectedElementContextMenu = new ObservableCollection<MenuItem>();
                }
                else
                {
                    _selectedElementContextMenu.Clear();
                }

                MenuItem editMenu = new MenuItem("Edit");
                editMenu.Children.Add(new MenuItem("Undo") { Command = UndoLastEditCommand, GestureText = "Ctrl+Z" });
                MenuItem actionsMenu = new MenuItem("Actions");
                MenuItem viewMenu = new MenuItem("View");

                actionsMenu.Children.Add(ExportMenu);
                actionsMenu.Children.Add(new MenuItem("Save As") { Command = DrawingSaveAsCommand, GestureText = "Ctrl+S" });
                actionsMenu.Children.Add(new MenuItem());
                MenuItem linkedAlnOps = new MenuItem("Linked Aln Operations");
                linkedAlnOps.Children.Add(new MenuItem("Clear In Memory Models") { Command = ClearInMemoryModelsCommand });
                linkedAlnOps.Children.Add(new MenuItem("Create Diagram By Template") { Command = CreateDiagramByTemplateCommand });
                actionsMenu.Children.Add(linkedAlnOps);

                viewMenu.Children.Add(new MenuItem("Switch Structure Model") { Command = SwitchStructureModelCommand });
                viewMenu.Children.Add(_synchronizeViewportItem);
                viewMenu.Children.Add(new MenuItem("Birds-eye Viewer") { Command = ShowBirdsEyeViewerCommand });
                viewMenu.Children.Add(new MenuItem("Property Inspector") { Command = ShowPropertyInspectorCommand });

                if (ElementSelectionMode == StructureElementSelectionMode.Nucleotide)
                {
                    MenuItem structureElementMenu = new MenuItem("Structure Element");
                    structureElementMenu.Children.Add(NucleotideElementEditMenu);
                    editMenu.Children.Add(structureElementMenu);
                }
                else if (ElementSelectionMode == StructureElementSelectionMode.BasePair)
                {
                    MenuItem structureElementMenu = new MenuItem("Structure Element");
                    structureElementMenu.Children.Add(BasePairElementEditMenu);
                    MenuItem structureModelMenu = new MenuItem("Structure Model");
                    MenuItem deleteBasePairActions = new MenuItem("Delete");
                    deleteBasePairActions.Children.Add(new MenuItem("Selected Base Pair") { Command = DeleteBasePairCommand });
                    structureModelMenu.Children.Add(deleteBasePairActions);
                    editMenu.Children.Add(structureModelMenu);
                    editMenu.Children.Add(structureElementMenu);
                }
                else if (ElementSelectionMode == StructureElementSelectionMode.Helix)
                {
                    MenuItem structureElementMenu = new MenuItem("Structure Element");
                    structureElementMenu.Children.Add(HelixElementEditMenu);
                    MenuItem structureModelMenu = new MenuItem("Structure Model");
                    MenuItem deleteHelixActions = new MenuItem("Delete");
                    deleteHelixActions.Children.Add(new MenuItem("Selected Helix") { Command = DeleteHelixCommand });
                    structureModelMenu.Children.Add(deleteHelixActions);
                    editMenu.Children.Add(structureModelMenu);
                    editMenu.Children.Add(structureElementMenu);
                }
                else if (ElementSelectionMode == StructureElementSelectionMode.Strand)
                {
                    MenuItem structureElementMenu = new MenuItem("Structure Element");
                    structureElementMenu.Children.Add(LoopElementEditMenu);
                    editMenu.Children.Add(structureElementMenu);
                }
                else if (ElementSelectionMode == StructureElementSelectionMode.Range)
                {
                    MenuItem rangeEditMenu = new MenuItem("Structure Element");
                    rangeEditMenu.Children.Add(RangeEditMenu);
                    MenuItem structureModelMenu = new MenuItem("Structure Model");
                    MenuItem deleteFromRangeActions = new MenuItem("Delete");
                    deleteFromRangeActions.Children.Add(new MenuItem("Base Pairs in Range or Domain") { Command = DeleteBasePairsFromRangeCommand });
                    structureModelMenu.Children.Add(deleteFromRangeActions);
                    editMenu.Children.Add(structureModelMenu);
                    editMenu.Children.Add(rangeEditMenu);
                }
                else if (ElementSelectionMode == StructureElementSelectionMode.Domain)
                {
                    MenuItem domainEditMenu = new MenuItem("Structure Element");
                    domainEditMenu.Children.Add(DomainEditMenu);
                    MenuItem structureModelMenu = new MenuItem("Structure Model");
                    MenuItem deleteFromRangeActions = new MenuItem("Delete");
                    deleteFromRangeActions.Children.Add(new MenuItem("Base Pairs in Range or Domain") { Command = DeleteBasePairsFromRangeCommand });
                    editMenu.Children.Add(structureModelMenu);
                    editMenu.Children.Add(domainEditMenu);
                }

                MenuItem visualDefaults = new MenuItem("Preferences");
                visualDefaults.Children.Add(new MenuItem("Nucleotide Visual Properties") { Command = EditDefaultNucleotideVisualPropertiesCommand });
                visualDefaults.Children.Add(new MenuItem("Base Pair Visual Properties") { Command = EditDefaultBasePairVisualPropertiesCommand });
                editMenu.Children.Add(new MenuItem());
                editMenu.Children.Add(visualDefaults);

                _selectedElementContextMenu.Add(editMenu);
                _selectedElementContextMenu.Add(actionsMenu);
                _selectedElementContextMenu.Add(viewMenu);

                return _selectedElementContextMenu;
            }
        }

        #region Commands

        public ICommand ShowPropertyInspectorCommand { get; private set; }
        public ICommand ShowBirdsEyeViewerCommand { get; private set; }
        public ICommand SwitchStructureModelCommand { get; private set; }
        public ICommand CreateDiagramByTemplateCommand { get; private set; }
        public ICommand ClearInMemoryModelsCommand { get; private set; }
        public ICommand ExportDiagramStrmlCommand { get; private set; }
        public ICommand ExportDiagramRNAmlCommand { get; private set; }
        public ICommand ExportDiagramBPSeqCommand { get; private set; }
        public ICommand ExportDiagramCTCommand { get; private set; }
        public ICommand ExportDiagramXRNACommand { get; private set; }
        public ICommand ToggleViewportSynchronizeCommand { get; private set; }
        public ICommand EditDefaultNucleotideVisualPropertiesCommand { get; private set; }
        public ICommand EditDefaultBasePairVisualPropertiesCommand { get; private set; }
        public ICommand DrawingSaveAsCommand { get; private set; }
        public ICommand UndoLastEditCommand { get; private set; }
        public ICommand DeleteBasePairCommand { get; private set; }
        public ICommand DeleteHelixCommand { get; private set; }
        public ICommand DeleteBasePairsFromRangeCommand { get; private set; }
        public ICommand AddBasePairCommand { get; private set; }
        public ICommand AddHelixCommand { get; private set; }
        public ICommand SnapHelixToOpeningBasePairCommand { get; private set; }
        public ICommand SnapHelixToClosingBasePairCommand { get; private set; }
        public ICommand SnapLoopCommand { get; private set; }
        public ICommand ChangeRangePropertiesCommand { get; private set; }
        public ICommand ChangeRangePropertiesFilterByNucleotideTypeCommand { get; private set; }
        public ICommand ChangeAllBasePairPropertiesFilterByTypeCommand { get; private set; }
        public ICommand ChangeAllBasePairsPropertiesCommand { get; private set; }
        public ICommand ChangeBasePairPropertiesCommand { get; private set; }
        public ICommand ChangeAllNucleotidePropertiesCommand { get; private set; }
        public ICommand ChangeAllNucleotidePropertiesFilterByTypeCommand { get; private set; }
        public ICommand ChangeNucleotidePropertiesCommand { get; private set; }
        public ICommand ResizeAllBasePairsCommand { get; private set; }
        public ICommand ResizeAllHelicesCommand { get; private set; }
        public ICommand ResizeStructureElementCommand { get; private set; }
        public ICommand RotateStructureElementRightCommand { get; private set; }
        public ICommand RotateStructureElementLeftCommand { get; private set; }
        public ICommand RotateStructureElementToCommand { get; private set; }
        public ICommand RotateStructureElementByCommand { get; private set; }
        public ICommand RotateStructureElementLeftIncrementCommand { get; private set; }
        public ICommand RotateStructureElementRightIncrementCommand { get; private set; }
        public ICommand TranslateStructureElementLeftCommand { get; private set; }
        public ICommand TranslateStructureElementRightCommand { get; private set; }
        public ICommand TranslateStructureElementUpCommand { get; private set; }
        public ICommand TranslateStructureElementDownCommand { get; private set; }
        public ICommand TranslateStructureElementToCommand { get; private set; }
        public ICommand TranslateStructureElementByCommand { get; private set; }

        #endregion

        public void RepositionViewportTo(double newLeft, double newTop)
        {
            Point maxTopLeft = new Point(CurrentCanvasSize.Width - CurrentViewportSize.Width, CurrentCanvasSize.Height - CurrentViewportSize.Height);
            double checkedLeftPos = newLeft;
            double checkedTopPos = newTop;

            //Validate the newLeft parameter.
            if (checkedLeftPos < 0.0)
                checkedLeftPos = 0.0;

            if (checkedLeftPos > maxTopLeft.X)
                checkedLeftPos = maxTopLeft.X;

            //Validate the newTop parameter.
            if (checkedTopPos < 0.0)
                checkedTopPos = 0.0;

            if (checkedTopPos > maxTopLeft.Y)
                checkedTopPos = maxTopLeft.Y;

            CurrentViewportTopLeft = new Point(checkedLeftPos, checkedTopPos);
            //_currentViewportLeft = checkedLeftPos;
            //_currentViewportTop = checkedTopPos;
            //OnPropertyChanged("CurrentViewportLeft", "CurrentViewportTop");
        }

        

        static SecondaryStructureEditorViewModel()
        {
            IUIVisualizer uiVisualizer = ViewModel.ServiceProvider.Resolve<IUIVisualizer>();
            if (uiVisualizer != null)
            {
                uiVisualizer.Register(typeof(SecondaryStructureEntitySelector).ToString(), typeof(SecondaryStructureEntitySelector));
                uiVisualizer.Register(typeof(TemplateDiagramTargetSequenceSelector).ToString(), typeof(TemplateDiagramTargetSequenceSelector));
                uiVisualizer.Register(typeof(StructureElementBrushEditor).ToString(), typeof(StructureElementBrushEditor));
                uiVisualizer.Register(typeof(DefaultNucleotideRenderingOptionsEditor).ToString(), typeof(DefaultNucleotideRenderingOptionsEditor));
                uiVisualizer.Register(typeof(SelectNucleotideForRenderingDefaultsEditor).ToString(), typeof(SelectNucleotideForRenderingDefaultsEditor));
                uiVisualizer.Register(typeof(DefaultBasePairRenderingOptionsEditor).ToString(), typeof(DefaultBasePairRenderingOptionsEditor));
                uiVisualizer.Register(typeof(SelectBasePairForRenderingDefaultsEditor).ToString(), typeof(SelectBasePairForRenderingDefaultsEditor));
                uiVisualizer.Register(typeof(PromptForValueEditor).ToString(), typeof(PromptForValueEditor));
                uiVisualizer.Register(typeof(PromptForSizingEditor).ToString(), typeof(PromptForSizingEditor));
                uiVisualizer.Register(typeof(ConfigureStructureElementVisualPropertiesEditor).ToString(), typeof(ConfigureStructureElementVisualPropertiesEditor));
                uiVisualizer.Register(typeof(BuildNucleotideTypeFilterEditor).ToString(), typeof(BuildNucleotideTypeFilterEditor));
                uiVisualizer.Register(typeof(BuildBasePairTypeFilterEditor).ToString(), typeof(BuildBasePairTypeFilterEditor));
                uiVisualizer.Register(typeof(PromptForStructureElementTranslationEditor).ToString(), typeof(PromptForStructureElementTranslationEditor));
                uiVisualizer.Register(typeof(PromptForNewHelixEditor).ToString(), typeof(PromptForNewHelixEditor));
                uiVisualizer.Register(typeof(PromptForNewBasePairEditor).ToString(), typeof(PromptForNewBasePairEditor));
            }
            _isBirdsEyeViewShowing = false;
            _isPropertyInspectorViewShowing = false;
        }

        public SecondaryStructureEditorViewModel()
        {
            RegisterWithMessageMediator();
            ReloadRuntimeNucleotideVisualRenderingOptions();
            ReloadRuntimeBasePairVisualRenderingOptions();
            _loadedStructures = new Dictionary<string, StructureModelData>();
            _synchronizeViewport = false;

            SwitchStructureModelCommand = new DelegatingCommand(OnSwitchStructureModel, () => (LoadedModelCount > 1));
            CreateDiagramByTemplateCommand = new DelegatingCommand(OnCreateNewDiagramByTemplate, () => (IsStructureModelSetLinked && !CurrentStructure.InMemoryDataOnly));
            ClearInMemoryModelsCommand = new DelegatingCommand(OnClearInMemoryModels, () => (InMemoryModelCount > 0 && !CurrentStructure.InMemoryDataOnly));
            ExportDiagramStrmlCommand = new DelegatingCommand(OnExportDiagramToStrmlFile, () => (CurrentStructure != null));
            ExportDiagramRNAmlCommand = new DelegatingCommand(OnExportDiagramToRNAmlFile, () => (CurrentStructure != null));
            ExportDiagramBPSeqCommand = new DelegatingCommand(OnExportDiagramToBPSeqFile, () => (CurrentStructure != null));
            ExportDiagramCTCommand = new DelegatingCommand(OnExportDiagramToCTFile, () => (CurrentStructure != null));
            ExportDiagramXRNACommand = new DelegatingCommand(OnExportDiagramToXRNAFile, () => (CurrentStructure != null));
            ShowBirdsEyeViewerCommand = new DelegatingCommand(OnShowBirdsEyeView, () => (CurrentStructure != null));
            ShowPropertyInspectorCommand = new DelegatingCommand(OnShowPropertyInspectorView, () => (CurrentStructure != null));
            ToggleViewportSynchronizeCommand = new DelegatingCommand(() => SynchronizeViewport = !SynchronizeViewport);
            EditDefaultNucleotideVisualPropertiesCommand = new DelegatingCommand(OnEditDefaultNucleotideVisualProperties, () => (CurrentStructure != null));
            EditDefaultBasePairVisualPropertiesCommand = new DelegatingCommand(OnEditDefaultBasePairVisualProperties, () => (CurrentStructure != null));
            DrawingSaveAsCommand = new DelegatingCommand(OnSaveDrawingAs, () => (CurrentStructure != null));
            UndoLastEditCommand = new DelegatingCommand(OnUndoLastEdit, () => (CurrentStructure != null && CurrentDrawingDirtyState));
            DeleteBasePairCommand = new DelegatingCommand(RaiseDeleteSelectedBasePairRequest, () => (CurrentStructure != null && ElementSelectionMode == StructureElementSelectionMode.BasePair && SelectedElement != null));
            DeleteHelixCommand = new DelegatingCommand(RaiseDeleteSelectedHelixRequest, () => (CurrentStructure != null && ElementSelectionMode == StructureElementSelectionMode.Helix && SelectedElement != null));
            DeleteBasePairsFromRangeCommand = new DelegatingCommand(RaiseDeleteBasePairsFromRangeRequest, () => (CurrentStructure != null && (ElementSelectionMode == StructureElementSelectionMode.Range || ElementSelectionMode == StructureElementSelectionMode.Domain)));
            AddBasePairCommand = new DelegatingCommand(OnCreateBasePair, () => (CurrentStructure != null));
            AddHelixCommand = new DelegatingCommand(OnCreateHelix, () => (CurrentStructure != null));
            ChangeRangePropertiesCommand = new DelegatingCommand(OnEditRangeProperties, () => (CurrentStructure != null && (ElementSelectionMode == StructureElementSelectionMode.Range || ElementSelectionMode == StructureElementSelectionMode.Domain)));
            ChangeRangePropertiesFilterByNucleotideTypeCommand = new DelegatingCommand(OnEditRangePropertiesFilterByNucleotideType, () => (CurrentStructure != null && (ElementSelectionMode == StructureElementSelectionMode.Range || ElementSelectionMode == StructureElementSelectionMode.Domain)));
            ChangeAllBasePairPropertiesFilterByTypeCommand = new DelegatingCommand(OnEditBasePairPropertiesFilterByType, () => (CurrentStructure != null));
            ChangeAllBasePairsPropertiesCommand = new DelegatingCommand(OnEditAllBasePairsProperties, () => (CurrentStructure != null));
            ChangeBasePairPropertiesCommand = new DelegatingCommand(OnEditBasePairProperties, () => (CurrentStructure != null && ElementSelectionMode == StructureElementSelectionMode.BasePair && SelectedElement != null));
            ChangeAllNucleotidePropertiesFilterByTypeCommand = new DelegatingCommand(OnEditNucleotidesPropertiesFilterByType, () => (CurrentStructure != null));
            ChangeAllNucleotidePropertiesCommand = new DelegatingCommand(OnEditAllNucleotidesProperties, () => (CurrentStructure != null));
            ChangeNucleotidePropertiesCommand = new DelegatingCommand(OnEditNucleotideProperties, () => (CurrentStructure != null && ElementSelectionMode == StructureElementSelectionMode.Nucleotide && SelectedElement != null));            
            SnapHelixToOpeningBasePairCommand = new DelegatingCommand(RaiseSnapSelectedHelixToOpeningBasePairRequest, () => (CurrentStructure != null && ElementSelectionMode == StructureElementSelectionMode.Helix && SelectedElement != null));
            SnapHelixToClosingBasePairCommand = new DelegatingCommand(RaiseSnapSelectedHelixToClosingBasePairRequest, () => (CurrentStructure != null && ElementSelectionMode == StructureElementSelectionMode.Helix && SelectedElement != null));
            ResizeAllBasePairsCommand = new DelegatingCommand(OnResizeAllBasePairs, () => CurrentStructure != null);
            ResizeAllHelicesCommand = new DelegatingCommand(OnResizeAllHelices, () => CurrentStructure != null);
            ResizeStructureElementCommand = new DelegatingCommand(OnResizeStructureElement, OnCanResizeStructureElement);
            TranslateStructureElementByCommand = new DelegatingCommand(OnTranslateStructureElementBy, CanTranslateStructureElement);
            TranslateStructureElementToCommand = new DelegatingCommand(OnTranslateStructureElementTo, CanTranslateStructureElement);
            TranslateStructureElementRightCommand = new DelegatingCommand(OnTranslateStructureElementRight, CanTranslateStructureElement);
            TranslateStructureElementLeftCommand = new DelegatingCommand(OnTranslateStructureElementLeft, CanTranslateStructureElement);
            TranslateStructureElementUpCommand = new DelegatingCommand(OnTranslateStructureElementUp, CanTranslateStructureElement);
            TranslateStructureElementDownCommand = new DelegatingCommand(OnTranslateStructureElementDown, CanTranslateStructureElement);
            RotateStructureElementLeftCommand = new DelegatingCommand(OnRotateStructureElementLeft, CanRotateStructureElement);
            RotateStructureElementLeftIncrementCommand = new DelegatingCommand(OnRotateStructureElementLeftIncrement, CanRotateStructureElement);
            RotateStructureElementRightCommand = new DelegatingCommand(OnRotateStructureElementRight, CanRotateStructureElement);
            RotateStructureElementRightIncrementCommand = new DelegatingCommand(OnRotateStructureElementRightIncrement, CanRotateStructureElement);
            RotateStructureElementToCommand = new DelegatingCommand(OnRotateStructureElementTo, CanRotateStructureElement);
            RotateStructureElementByCommand = new DelegatingCommand(OnRotateStructureElementBy, CanRotateStructureElement);

            EditMenu.Add(new MenuItem("Undo") { Command = UndoLastEditCommand, GestureText="Ctrl+Z" });
            //EditMenu.Add(new MenuItem("Undo All"));
           
            MenuItem modelActions = new MenuItem("Structure Model");
            MenuItem addActions = new MenuItem("Add");
            addActions.Children.Add(new MenuItem("Base Pair...") { Command = AddBasePairCommand });
            addActions.Children.Add(new MenuItem("Helix...") { Command = AddHelixCommand });
            MenuItem deleteActions = new MenuItem("Delete");
            deleteActions.Children.Add(new MenuItem("Selected Base Pair") { Command = DeleteBasePairCommand });
            deleteActions.Children.Add(new MenuItem("Selected Helix") { Command = DeleteHelixCommand });
            deleteActions.Children.Add(new MenuItem("Base Pairs in Range or Domain") { Command = DeleteBasePairsFromRangeCommand });
            modelActions.Children.Add(addActions);
            modelActions.Children.Add(deleteActions);
            EditMenu.Add(modelActions);
            EditMenu.Add(StructureElementsEditMenu);

            MenuItem visualDefaults = new MenuItem("Preferences");
            visualDefaults.Children.Add(new MenuItem("Nucleotide Visual Properties") { Command = EditDefaultNucleotideVisualPropertiesCommand });
            visualDefaults.Children.Add(new MenuItem("Base Pair Visual Properties") { Command = EditDefaultBasePairVisualPropertiesCommand });
            EditMenu.Add(new MenuItem());
            EditMenu.Add(visualDefaults);

            ActionsMenu.Add(ExportMenu);
            ActionsMenu.Add(new MenuItem("Save As") { Command = DrawingSaveAsCommand, GestureText="Ctrl+S" });
            ActionsMenu.Add(new MenuItem());
            MenuItem linkedAlnOps = new MenuItem("Linked Aln Operations");
            linkedAlnOps.Children.Add(new MenuItem("Clear In Memory Models") { Command = ClearInMemoryModelsCommand });
            linkedAlnOps.Children.Add(new MenuItem("Create Diagram By Template") { Command = CreateDiagramByTemplateCommand });
            ActionsMenu.Add(linkedAlnOps);
            
            ViewMenu.Add(new MenuItem("Switch Structure Model") { Command = SwitchStructureModelCommand });
            _synchronizeViewportItem = new MenuItem("Center Viewport on Selection") { Command = ToggleViewportSynchronizeCommand, IsChecked = SynchronizeViewport };
            ViewMenu.Add(_synchronizeViewportItem);
            ViewMenu.Add(new MenuItem("Birds-eye Viewer") { Command = ShowBirdsEyeViewerCommand });
            ViewMenu.Add(new MenuItem("Property Inspector") { Command = ShowPropertyInspectorCommand });

            _commLinkProxy = new LinkedSecondaryStructureEditorProxy(this);
        }

        public override bool Initialize(IBioDataLoader data)
        {
            var structureProvider = data as IBioDataLoader<IStructureModelBioEntity>;
            if (structureProvider != null && structureProvider.Entities != null && structureProvider.Entities.Count > 0)
            {
                foreach (var entity in structureProvider.Entities)
                {
                    StructureModelData structureEntity = new StructureModelData();
                    structureEntity.Entity = new EditableStructureModel(entity);
                    structureEntity.InMemoryDataOnly = false;
                    if (structureEntity.IsLinkable)
                    {
                        structureEntity.Title = string.Format("Structure Editor: rCAD Source, Scientific Name: {0}", structureEntity.Entity.Sequence.ScientificName);
                        structureEntity.Key = string.Format("{0}::{1}::{2}", structureEntity.Entity.Sequence.Id, structureEntity.Entity.Sequence.SourceId, structureEntity.Entity.Sequence.ScientificName);
                    }
                    else
                    {
                        structureEntity.Title = string.Format("Structure Editor: {0}", structureProvider.InitializationData);
                        structureEntity.Key = structureProvider.InitializationData;
                    }
                    _loadedStructures.Add(structureEntity.Key, structureEntity);
                }

                CurrentStructure = _loadedStructures.Values.First();
                RequestAlignmentViewLink();
                return base.Initialize(data);
            }
            return false;
        }

        public override void RaiseCloseRequest()
        {
            if (IsStructureModelSetLinked)
            {
                DisconnectFromLinkedAlignmentView(_linkedAlignment.ID);
                BroadcastStructureViewClosing();
            }

            SendMessage(SecondaryStructureEditorViewMessages.SecondaryStructureEditorViewClosingMessage, this);
            base.RaiseCloseRequest();
        }

        protected override void OnActivate()
        {
            if (IsBirdsEyeViewShowing)
                BirdsEyeViewModel.SetActiveViewModel(this);

            if (IsPropertyInspectorViewShowing)
                PropertyInspectorViewModel.SetActiveViewModel(this);

            SendMessage(SecondaryStructureEditorViewMessages.SecondaryStructureEditorViewChangedMessage, this);
            base.OnActivate();
        }

        /// <summary>
        /// This turns the Birds-Eye-Viewer on and off
        /// </summary>
        private void OnShowBirdsEyeView()
        {
            IsBirdsEyeViewShowing = !IsBirdsEyeViewShowing;
            SecondaryStructureEditorBirdsEyeViewModel bev = BirdsEyeViewModel;
            if (IsBirdsEyeViewShowing)
            {
                bev.Show();
                bev.SetActiveViewModel(this);
            }
            else
            {
                bev.CloseView.Execute(null);
            }
        }

        /// <summary>
        /// This turns the Property Inspector on
        /// </summary>
        private void OnShowPropertyInspectorView()
        {
            IsPropertyInspectorViewShowing = !IsPropertyInspectorViewShowing;
            SecondaryStructureEditorVisualComponentInspectorViewModel piv = PropertyInspectorViewModel;
            if (IsPropertyInspectorViewShowing)
            {
                piv.Show();
                piv.SetActiveViewModel(this);
            }
            else
            {
                piv.CloseView.Execute(null);
            }
        }

        //This method requests a link to an available, corresponding alignment view
        private void RequestAlignmentViewLink()
        {
            if (IsStructureModelSetLinkable && !IsStructureModelSetLinked)
                SendMessage<AlignmentViewLinkRequestMessage>(LinkedViewsMessages.LinkableStructureViewOpeningRequest, new AlignmentViewLinkRequestMessage(_commLinkProxy, RCADAlignmentId));
        }

        //This method notifies any potential linked alignment view that we are closing
        private void BroadcastStructureViewClosing()
        {
            SendMessage<LinkedViewClosingNotification>(LinkedViewsMessages.LinkedStructureViewClosingNotification, new LinkedViewClosingNotification(_commLinkProxy.ID));
        }
        
        //This method handles the response from a potential linkable alignment view to our request for alignment view link.
        [MessageMediatorTarget(LinkedViewsMessages.LinkableStructureViewOpeningResponse)]
        private void HandleStructureViewLinkResponse(AlignmentViewLinkResponseMessage response)
        {
            if (response != null)
            {
                ConnectWithLinkableAlignmentView(RCADAlignmentId, response.Link);
            }
        }

        //This method handles a request from an open alignment view for a link to us.
        [MessageMediatorTarget(LinkedViewsMessages.LinkableAlignmentViewOpeningRequest)]
        private void HandleAlignmentViewLinkRequest(StructureViewLinkRequestMessage request)
        {
            if (request != null)
            {
                if(ConnectWithLinkableAlignmentView(request.AlignmentId, request.AlignmentView))
                    SendMessage<StructureViewLinkResponseMessage>(LinkedViewsMessages.LinkableAlignmentViewOpeningResponse, new StructureViewLinkResponseMessage(_commLinkProxy));
            }
        }

        //This method handles a broadcast message from a alignment view that is closing and is linked to any structure view
        [MessageMediatorTarget(LinkedViewsMessages.LinkedAlignmentViewClosingNotification)]
        private void HandleLinkedAlignmentViewClosingNotification(LinkedViewClosingNotification notification)
        {
            if (notification != null)
            {
                DisconnectFromLinkedAlignmentView(notification.ViewID);   
            }
        }

        private bool ConnectWithLinkableAlignmentView(string linkedAlignmentID, ILinkableAlignmentView linkedAlignmentView)
        {
            if (!string.IsNullOrEmpty(linkedAlignmentID) &&
                !string.IsNullOrEmpty(RCADAlignmentId) &&
                IsStructureModelSetLinkable &&
                !IsStructureModelSetLinked &&
                linkedAlignmentID.Equals(RCADAlignmentId))
            {
                _linkedAlignment = linkedAlignmentView;
                _linkedAlignment.FocusedColumnIndexChanged += new EventHandler(OnLinkedAlignmentViewFocusedColumnChanged);
                _linkedAlignment.FocusedRowChanged += new EventHandler(OnLinkedAlignmentViewFocusedRowChanged);
                OnPropertyChanged("IsStructureModelSetLinked", "IsCurrentStructureLinked");
                return true;
            }
            return false;
        }

        private void DisconnectFromLinkedAlignmentView(string alignmentViewID)
        {
            if (IsStructureModelSetLinked &&
                !string.IsNullOrEmpty(alignmentViewID) &&
                _linkedAlignment.ID.Equals(alignmentViewID))
            {
                _linkedAlignment.FocusedColumnIndexChanged -= new EventHandler(OnLinkedAlignmentViewFocusedColumnChanged);
                _linkedAlignment.FocusedRowChanged -= new EventHandler(OnLinkedAlignmentViewFocusedRowChanged);
                _linkedAlignment = null;
                OnPropertyChanged("IsStructureModelSetLinked", "IsCurrentStructureLinked");
            }
        }

        private void OnLinkedAlignmentViewFocusedRowChanged(object sender, EventArgs e)
        {
            HandleLinkedAlignmentViewUpdate();
            OnPropertyChanged("IsCurrentStructureLinked");
        }

        private void OnLinkedAlignmentViewFocusedColumnChanged(object sender, EventArgs e)
        {
            HandleLinkedAlignmentViewUpdate();
        }

        private void HandleLinkedAlignmentViewUpdate()
        {
            if (IsCurrentStructureLinked && 
                ElementSelectionMode != StructureElementSelectionMode.None && 
                ElementSelectionMode != StructureElementSelectionMode.Range &&
                ElementSelectionMode != StructureElementSelectionMode.Domain)
            {
                int currentFocusedNtIndex = _linkedAlignment.GetCurrentFocusedSequenceIndex();
                if (currentFocusedNtIndex >= 0 && currentFocusedNtIndex < CurrentStructure.Entity.Sequence.SequenceData.Count)
                {
                    RaiseSelectStructureElementContainingNucleotideIndexRequest(currentFocusedNtIndex);
                }
            }
        }

        private void LoadCurrentStructureMetadata()
        {
            NucleotideRenderingStyle = null; //We make sure its set with something
            NucleotideFontFamily = null;
            NucleotideFontStyle = null;
            NucleotideFontWeight = null;
            NucleotideFontSize = null;
            NucleotideFill = null;
            NucleotideCircleDiameter = null;
            NucleotideCircleEdgeThickness = null;
            NucleotideCircleFill = null;
            NucleotideCircleIsFilled = null;
            NucleotideCircleStroke = null;
            BasePairConnectorFill = null;
            BasePairConnectorIsFilled = null;
            BasePairConnectorIsVisible = null;
            BasePairConnectorRenderingStyle = null;
            BasePairConnectorSize = null;
            BasePairConnectorStroke = null;
            BasePairConnectorStrokeThickness = null;

            if (CurrentStructure == null)
                return;

            if (CurrentStructure.Entity == null)
                return;

            StructureModelMetadata structureModelProperties = (CurrentStructure.Entity.StructureModelPropertyEdits != null) ? CurrentStructure.Entity.StructureModelPropertyEdits :
                CurrentStructure.Entity.StructureModelProperties;

            if (structureModelProperties != null)
            {
                if (structureModelProperties.NucleotideTextRendering != null)
                {
                    if (structureModelProperties.NucleotideTextRendering.Color != null)
                    {
                        try { NucleotideFill = new SolidColorBrush((Color)colorChecker.ConvertFrom(structureModelProperties.NucleotideTextRendering.Color)); }
                        catch { }
                    }

                    if (structureModelProperties.NucleotideTextRendering.Font != null)
                    {
                        try { NucleotideFontFamily = (FontFamily)fontFamilyChecker.ConvertFrom(structureModelProperties.NucleotideTextRendering.Font); }
                        catch { }
                    }

                    if (structureModelProperties.NucleotideTextRendering.FontStyle != null)
                    {
                        try { NucleotideFontStyle = (FontStyle)fontStyleChecker.ConvertFrom(structureModelProperties.NucleotideTextRendering.FontStyle); }
                        catch { }
                    }

                    if (structureModelProperties.NucleotideTextRendering.FontWeight != null)
                    {
                        try { NucleotideFontWeight = (FontWeight)fontWeightChecker.ConvertFrom(structureModelProperties.NucleotideTextRendering.FontWeight); }
                        catch { }
                    }

                    if (structureModelProperties.NucleotideTextRendering.FontSize > 0.0)
                    {
                        NucleotideFontSize = structureModelProperties.NucleotideTextRendering.FontSize;
                    }
                }

                if (structureModelProperties.NucleotideCircleRendering != null)
                {
                    if (structureModelProperties.NucleotideCircleRendering.Diameter > 0.0)
                    {
                        NucleotideCircleDiameter = structureModelProperties.NucleotideCircleRendering.Diameter;
                    }

                    if (structureModelProperties.NucleotideCircleRendering.EdgeThickness > 0.0)
                    {
                        NucleotideCircleEdgeThickness = structureModelProperties.NucleotideCircleRendering.EdgeThickness;
                    }

                    if (structureModelProperties.NucleotideCircleRendering.EdgeColor != null)
                    {
                        try { NucleotideCircleStroke = new SolidColorBrush((Color)colorChecker.ConvertFrom(structureModelProperties.NucleotideCircleRendering.EdgeColor)); }
                        catch { }
                    }

                    if (structureModelProperties.NucleotideCircleRendering.FillColor != null)
                    {
                        try { NucleotideCircleFill = new SolidColorBrush((Color)colorChecker.ConvertFrom(structureModelProperties.NucleotideCircleRendering.FillColor)); }
                        catch { }
                    }

                    NucleotideCircleIsFilled = structureModelProperties.NucleotideCircleRendering.Filled;
                }

                if (structureModelProperties.NucleotideRenderingMode.HasValue)
                {
                    if (structureModelProperties.NucleotideRenderingMode.Value == NucleotideMetadata.RenderingMode.Text)
                        NucleotideRenderingStyle = NucleotideRenderingMode.Text;
                    else if (structureModelProperties.NucleotideRenderingMode.Value == NucleotideMetadata.RenderingMode.Circle)
                        NucleotideRenderingStyle = NucleotideRenderingMode.Circle;
                    else
                        NucleotideRenderingStyle = null;
                }

                if (structureModelProperties.ConnectorStyle.HasValue)
                {
                    if (structureModelProperties.ConnectorStyle.Value == BasePairMetadata.ConnectorRenderingMode.Circle)
                        BasePairConnectorRenderingStyle = BasePairConnectorRenderingMode.Circle;
                    else if (structureModelProperties.ConnectorStyle.Value == BasePairMetadata.ConnectorRenderingMode.Line)
                        BasePairConnectorRenderingStyle = BasePairConnectorRenderingMode.Line;
                    else if (structureModelProperties.ConnectorStyle.Value == BasePairMetadata.ConnectorRenderingMode.Rectangle)
                        BasePairConnectorRenderingStyle = BasePairConnectorRenderingMode.Rectangle;
                    else
                        BasePairConnectorRenderingStyle = null;
                }

                if (structureModelProperties.ConnectorVisible.HasValue)
                {
                    BasePairConnectorIsVisible = structureModelProperties.ConnectorVisible.Value;
                }

                if (structureModelProperties.ConnectorRendering != null)
                {
                    if (structureModelProperties.ConnectorRendering.Size > 0.0)
                        BasePairConnectorSize = structureModelProperties.ConnectorRendering.Size;

                    if (structureModelProperties.ConnectorRendering.EdgeThickness > 0.0)
                        BasePairConnectorStrokeThickness = structureModelProperties.ConnectorRendering.EdgeThickness;

                    if (structureModelProperties.ConnectorRendering.FillColor != null)
                    {
                        try { BasePairConnectorFill = new SolidColorBrush((Color)colorChecker.ConvertFrom(structureModelProperties.ConnectorRendering.FillColor)); }
                        catch { }
                    }

                    if (structureModelProperties.ConnectorRendering.StrokeColor != null)
                    {
                        try { BasePairConnectorStroke = new SolidColorBrush((Color)colorChecker.ConvertFrom(structureModelProperties.ConnectorRendering.StrokeColor)); }
                        catch { }
                    }

                    if (structureModelProperties.ConnectorRendering.Filled.HasValue)
                    {
                        BasePairConnectorIsFilled = structureModelProperties.ConnectorRendering.Filled;
                    }
                }
            }
        }

        private void OnCreateHelix()
        {
            if (CurrentStructure != null)
            {
                PromptForNewHelixViewModel vm = new PromptForNewHelixViewModel();
                IUIVisualizer uiVisualizer = Resolve<IUIVisualizer>();
                if (uiVisualizer != null)
                {
                    bool? result = uiVisualizer.ShowDialog(typeof(PromptForNewHelixEditor).ToString(), vm);
                    if (result != null && result.Value)
                    {
                        RaiseCreateHelixRequest(vm.FivePrimeStart - 1, vm.ThreePrimeEnd - 1, vm.FivePrimeEnd - 1);
                    }
                }
            }
        }

        private void OnCreateBasePair()
        {
            if (CurrentStructure != null)
            {
                PromptForNewBasePairViewModel vm = new PromptForNewBasePairViewModel();
                IUIVisualizer uiVisualizer = Resolve<IUIVisualizer>();
                if (uiVisualizer != null)
                {
                    bool? result = uiVisualizer.ShowDialog(typeof(PromptForNewBasePairEditor).ToString(), vm);
                    if (result != null && result.Value)
                    {
                        RaiseCreateBasePairRequest(vm.FivePrime - 1, vm.ThreePrime - 1);
                    }
                }
            }
        }

        private void OnEditRangeProperties()
        {
            if (CurrentStructure != null &&
               (ElementSelectionMode == StructureElementSelectionMode.Range || ElementSelectionMode == StructureElementSelectionMode.Domain))
            {
                ConfigureStructureElementVisualPropertiesViewModel vm = new ConfigureStructureElementVisualPropertiesViewModel(true) { ShowCreateFilter = false };
                IUIVisualizer uiVisualizer = Resolve<IUIVisualizer>();
                if (uiVisualizer != null)
                {
                    bool? result = uiVisualizer.ShowDialog(typeof(ConfigureStructureElementVisualPropertiesEditor).ToString(), vm);
                    if (result != null && result.Value)
                    {
                        RaiseEditSelectedRangeStructureElementPropertiesRequest(vm.Changes, null);
                    }
                }
            }
        }

        private void OnEditRangePropertiesFilterByNucleotideType()
        {
            if (CurrentStructure != null &&
              (ElementSelectionMode == StructureElementSelectionMode.Range || ElementSelectionMode == StructureElementSelectionMode.Domain))
            {
                ConfigureStructureElementVisualPropertiesViewModel vm = new ConfigureStructureElementVisualPropertiesViewModel(true) { ShowCreateFilter = true };
                IUIVisualizer uiVisualizer = Resolve<IUIVisualizer>();
                if (uiVisualizer != null)
                {
                    bool? result = uiVisualizer.ShowDialog(typeof(ConfigureStructureElementVisualPropertiesEditor).ToString(), vm);
                    if (result != null && result.Value)
                    {
                        RaiseEditSelectedRangeStructureElementPropertiesRequest(vm.Changes, vm.NucleotideFilter);
                    }
                }
            }
        }

        private void OnEditNucleotideProperties()
        {
            if (CurrentStructure != null &&
                ElementSelectionMode == StructureElementSelectionMode.Nucleotide &&
                SelectedElement != null)
            {
                ConfigureStructureElementVisualPropertiesViewModel vm = new ConfigureStructureElementVisualPropertiesViewModel(true) { ShowCreateFilter = false };
                IUIVisualizer uiVisualizer = Resolve<IUIVisualizer>();
                if (uiVisualizer != null)
                {
                    bool? result = uiVisualizer.ShowDialog(typeof(ConfigureStructureElementVisualPropertiesEditor).ToString(), vm);
                    if (result != null && result.Value)
                    {
                        RaiseEditSelectedStructureElementPropertiesRequest(vm.Changes);
                    }
                }
            }
        }

        private void OnEditNucleotidesPropertiesFilterByType()
        {
            if (CurrentStructure != null)
            {
                ConfigureStructureElementVisualPropertiesViewModel vm = new ConfigureStructureElementVisualPropertiesViewModel(true) { ShowCreateFilter = true };
                IUIVisualizer uiVisualizer = Resolve<IUIVisualizer>();
                if (uiVisualizer != null)
                {
                    bool? result = uiVisualizer.ShowDialog(typeof(ConfigureStructureElementVisualPropertiesEditor).ToString(), vm);
                    if (result != null && result.Value)
                    {
                        RaiseEditNucleotideElementPropertiesRequest(vm.Changes, vm.NucleotideFilter);
                    }
                }
            }
        }

        private void OnEditAllNucleotidesProperties()
        {
            if (CurrentStructure != null)
            {
                ConfigureStructureElementVisualPropertiesViewModel vm = new ConfigureStructureElementVisualPropertiesViewModel(true) { ShowCreateFilter = false };
                IUIVisualizer uiVisualizer = Resolve<IUIVisualizer>();
                if (uiVisualizer != null)
                {
                    bool? result = uiVisualizer.ShowDialog(typeof(ConfigureStructureElementVisualPropertiesEditor).ToString(), vm);
                    if (result != null && result.Value)
                    {
                        RaiseEditNucleotideElementPropertiesRequest(vm.Changes, vm.NucleotideFilter);
                    }
                }
            }
        }

        private void OnEditBasePairProperties()
        {
            if (CurrentStructure != null &&
                ElementSelectionMode == StructureElementSelectionMode.BasePair &&
                SelectedElement != null)
            {
                ConfigureStructureElementVisualPropertiesViewModel vm = new ConfigureStructureElementVisualPropertiesViewModel(false) { ShowCreateFilter = false };
                IUIVisualizer uiVisualizer = Resolve<IUIVisualizer>();
                if (uiVisualizer != null)
                {
                    bool? result = uiVisualizer.ShowDialog(typeof(ConfigureStructureElementVisualPropertiesEditor).ToString(), vm);
                    if (result != null && result.Value)
                    {
                        RaiseEditSelectedStructureElementPropertiesRequest(vm.Changes);
                    }
                }   
            }
        }

        private void OnEditBasePairPropertiesFilterByType()
        {
            if (CurrentStructure != null)
            {
                ConfigureStructureElementVisualPropertiesViewModel vm = new ConfigureStructureElementVisualPropertiesViewModel(false) { ShowCreateFilter = true };
                IUIVisualizer uiVisualizer = Resolve<IUIVisualizer>();
                if (uiVisualizer != null)
                {
                    bool? result = uiVisualizer.ShowDialog(typeof(ConfigureStructureElementVisualPropertiesEditor).ToString(), vm);
                    if (result != null && result.Value)
                    {
                        RaiseEditBasePairElementPropertiesRequest(vm.Changes, vm.BasePairFilter);
                    }
                }
            }
        }

        private void OnEditAllBasePairsProperties()
        {
            if (CurrentStructure != null)
            {
                ConfigureStructureElementVisualPropertiesViewModel vm = new ConfigureStructureElementVisualPropertiesViewModel(false) { ShowCreateFilter = false };
                IUIVisualizer uiVisualizer = Resolve<IUIVisualizer>();
                if (uiVisualizer != null)
                {
                    bool? result = uiVisualizer.ShowDialog(typeof(ConfigureStructureElementVisualPropertiesEditor).ToString(), vm);
                    if (result != null && result.Value)
                    {
                        RaiseEditBasePairElementPropertiesRequest(vm.Changes, vm.BasePairFilter);
                    }
                }
            }
        }

        private bool OnCanResizeStructureElement()
        {
            return (CurrentStructure != null &&
                   (ElementSelectionMode == StructureElementSelectionMode.Helix || ElementSelectionMode == StructureElementSelectionMode.BasePair || 
                    ElementSelectionMode == StructureElementSelectionMode.Strand) &&
                    SelectedElement != null) ? true : false;
        }

        private void OnResizeStructureElement()
        {
            if (CurrentStructure != null &&
                SelectedElement != null)
            {
                if (ElementSelectionMode == StructureElementSelectionMode.BasePair)
                {
                    PromptForPositiveValueViewModel vm = new PromptForPositiveValueViewModel()
                    {
                        Title = "Change Selected Base Pair Width",
                        Instructions = "Enter a new Width for the selected Base Pair"
                    };
                    IUIVisualizer uiVisualizer = Resolve<IUIVisualizer>();
                    if (uiVisualizer != null)
                    {
                        bool? result = uiVisualizer.ShowDialog(typeof(PromptForValueEditor).ToString(), vm);
                        if (result != null && result.Value)
                        {
                            RaiseResizeSelectedStructureElementRequest(false, true, 0.0, vm.Value);
                        }
                    }
                }
                else if (ElementSelectionMode == StructureElementSelectionMode.Helix)
                {
                    PromptForSizingViewModel vm = new PromptForSizingViewModel()
                    {
                        Title = "Change Selected Helix Width and/or Padding",
                        Instructions = "Enter a new Width and/or Padding for the Selected Helix"
                    };
                    IUIVisualizer uiVisualizer = Resolve<IUIVisualizer>();
                    if (uiVisualizer != null)
                    {
                        bool? result = uiVisualizer.ShowDialog(typeof(PromptForSizingEditor).ToString(), vm);
                        if (result != null && result.Value)
                        {
                            RaiseResizeSelectedStructureElementRequest(vm.HavePadding, vm.HaveWidth, vm.Padding, vm.Width);
                        }
                    }
                }
                else if (ElementSelectionMode == StructureElementSelectionMode.Strand)
                {
                    PromptForSizingViewModel vm = new PromptForSizingViewModel()
                    {
                        Title = "Change Selected Loop Width and/or Height",
                        Instructions = "Enter a new Width and/or Height for the Selected Loop",
                        PaddingLabel = "Height"
                    };
                    IUIVisualizer uiVisualizer = Resolve<IUIVisualizer>();
                    if (uiVisualizer != null)
                    {
                        bool? result = uiVisualizer.ShowDialog(typeof(PromptForSizingEditor).ToString(), vm);
                        if (result != null && result.Value)
                        {
                            RaiseResizeSelectedStructureElementRequest(vm.HavePadding, vm.HaveWidth, vm.Padding, vm.Width);
                        }
                    }
                }
            }
        }

        private void OnResizeAllHelices()
        {
            if (CurrentStructure != null)
            {
                PromptForSizingViewModel vm = new PromptForSizingViewModel()
                {
                    Title = "Change Width and/or Padding for All Helices",
                    Instructions = "Enter a new Width and/or Padding for all Helices"
                };
                IUIVisualizer uiVisualizer = Resolve<IUIVisualizer>();
                if (uiVisualizer != null)
                {
                    bool? result = uiVisualizer.ShowDialog(typeof(PromptForSizingEditor).ToString(), vm);
                    if (result != null && result.Value)
                    {
                        RaiseResizeAllHelicesRequest(vm.HavePadding, vm.HaveWidth, vm.Padding, vm.Width);
                    }
                }
            }
        }

        private void OnResizeAllBasePairs()
        {
            if (CurrentStructure != null)
            {
                PromptForPositiveValueViewModel vm = new PromptForPositiveValueViewModel()
                {
                    Title = "Change Width for all Base Pairs",
                    Instructions = "Enter a new Width for all Base Pairs"
                };
                IUIVisualizer uiVisualizer = Resolve<IUIVisualizer>();
                if (uiVisualizer != null)
                {
                    bool? result = uiVisualizer.ShowDialog(typeof(PromptForValueEditor).ToString(), vm);
                    if (result != null && result.Value)
                    {
                        RaiseResizeAllBasePairsRequest(vm.Value);
                    }
                }
            }
        }

        private bool CanTranslateStructureElement()
        {
            return (CurrentStructure != null && 
                    ElementSelectionMode != StructureElementSelectionMode.None && 
                    SelectedElement != null) ? true : false;
        }

        private void OnTranslateStructureElementBy()
        {
            if (CanTranslateStructureElement())
            {
                PromptForStructureElementTranslationViewModel vm = new PromptForStructureElementTranslationViewModel()
                {
                    WantDeltas = true,
                    Instructions = "Enter X and Y deltas to move the selected Structure Element By",
                    Title = "Move the selected Structure Element",
                    DeltaX = 0.0,
                    DeltaY = 0.0
                };
                IUIVisualizer uiVisualizer = Resolve<IUIVisualizer>();
                if (uiVisualizer != null)
                {
                    bool? result = uiVisualizer.ShowDialog(typeof(PromptForStructureElementTranslationEditor).ToString(), vm);
                    if (result != null && result.Value)
                    {
                        if (vm.IsValid)
                            RaiseTranslateSelectedStructureElementRequest(new Point(vm.DeltaX, vm.DeltaY), TranslationType.ByOffset);
                    }
                }
            }
        }

        private void OnTranslateStructureElementTo()
        {
            if (CanTranslateStructureElement())
            {
                PromptForStructureElementTranslationViewModel vm = new PromptForStructureElementTranslationViewModel()
                {
                    WantDeltas = false,
                    Instructions = "Enter X and Y cooridinates for the selected Structure Element",
                    Title = "Move the selected Structure Element",
                    LocationX = 0.0,
                    LocationY = 0.0
                };
                IUIVisualizer uiVisualizer = Resolve<IUIVisualizer>();
                if (uiVisualizer != null)
                {
                    bool? result = uiVisualizer.ShowDialog(typeof(PromptForStructureElementTranslationEditor).ToString(), vm);
                    if (result != null && result.Value)
                    {
                        if(vm.IsValid)
                            RaiseTranslateSelectedStructureElementRequest(new Point(vm.LocationX, vm.LocationY), TranslationType.ToLocation);
                    }
                }
            }
        }

        private void OnTranslateStructureElementRight()
        {
            if (CanTranslateStructureElement())
            {
                RaiseTranslateSelectedStructureElementRequest(new Point(1, 0), TranslationType.ByOffset);
            }
        }

        private void OnTranslateStructureElementLeft()
        {
            if (CanTranslateStructureElement())
            {
                RaiseTranslateSelectedStructureElementRequest(new Point(-1, 0), TranslationType.ByOffset);
            }
        }

        private void OnTranslateStructureElementUp()
        {
            if (CanTranslateStructureElement())
            {
                RaiseTranslateSelectedStructureElementRequest(new Point(0, -1), TranslationType.ByOffset);
            }
        }

        private void OnTranslateStructureElementDown()
        {
            if (CanTranslateStructureElement())
            {
                RaiseTranslateSelectedStructureElementRequest(new Point(0, 1), TranslationType.ByOffset);
            }
        }

        private bool CanRotateStructureElement()
        {
            return (CurrentStructure != null &&
                   (ElementSelectionMode == StructureElementSelectionMode.BasePair || ElementSelectionMode == StructureElementSelectionMode.Helix) &&
                    SelectedElement != null) ? true : false;
        }

        private void OnRotateStructureElementBy()
        {
            if (CanRotateStructureElement())
            {
                PromptForRotationViewModel vm = new PromptForRotationViewModel()
                {
                    Title = "Rotate Selected Structure Element by Specified Amount"
                };
                IUIVisualizer uiVisualizer = Resolve<IUIVisualizer>();
                if (uiVisualizer != null)
                {
                    bool? result = uiVisualizer.ShowDialog(typeof(PromptForValueEditor).ToString(), vm);
                    if (result != null && result.Value)
                    {
                        RaiseRotateSelectedStructureElementRequest(vm.Value, RotationType.ByAngleDegrees);
                    }
                }
            }
        }

        private void OnRotateStructureElementTo()
        {
            if (CanRotateStructureElement())
            {
                PromptForAngleViewModel vm = new PromptForAngleViewModel()
                {
                    Title = "Rotate Selected Structure Element To Angle"
                };
                IUIVisualizer uiVisualizer = Resolve<IUIVisualizer>();
                if (uiVisualizer != null)
                {
                    bool? result = uiVisualizer.ShowDialog(typeof(PromptForValueEditor).ToString(), vm);
                    if (result != null && result.Value)
                    {
                        RaiseRotateSelectedStructureElementRequest(vm.Value, RotationType.ToAngleDegrees);
                    }
                }
            }
        }

        private void OnRotateStructureElementLeftIncrement()
        {
            if (CanRotateStructureElement())
            {
                RaiseRotateSelectedStructureElementRequest(1.0, RotationType.ByAngleDegrees);
            }
        }

        private void OnRotateStructureElementLeft()
        {
            if (CanRotateStructureElement())
            {
                RaiseRotateSelectedStructureElementRequest(90.0, RotationType.ByAngleDegrees);
            }
        }

        private void OnRotateStructureElementRightIncrement()
        {
            if (CanRotateStructureElement())
            {
                RaiseRotateSelectedStructureElementRequest(-1.0, RotationType.ByAngleDegrees);
            }
        }

        private void OnRotateStructureElementRight()
        {
            if (CanRotateStructureElement())
            {
                RaiseRotateSelectedStructureElementRequest(-90.0, RotationType.ByAngleDegrees);
            }
        }
        
        private void OnUndoLastEdit()
        {
            if (CurrentStructure != null)
            {
                RaiseUndoLastEditRequest();
            }
        }

        private void OnSaveDrawingAs()
        {
            if (CurrentStructure != null)
            {
                RaiseSynchronizeStructureModelRequest();
                string outputFileName = PromptForOutputFileName(".strml"); //This is since we don't "write back" to the open file yet, so in essence, this is a save as
                if (outputFileName != null)
                {
                    try
                    {
                        ExportDiagramToStrml.ExportDiagram(CurrentStructure, outputFileName);
                    }
                    catch (Exception e)
                    {
                        IErrorVisualizer errMesg = Resolve<IErrorVisualizer>();
                        errMesg.Show("Error",
                            string.Format("Error: Failed exporting current diagram to file {0}, details: \n{1}\n{2}", outputFileName, e.Message, e.StackTrace));
                        outputFileName = null;
                    }
                }
            }
        }

        private void OnEditDefaultBasePairVisualProperties()
        {
            if (CurrentStructure != null)
            {
                BasePairVisualRenderingDefaultsViewModel vm = new BasePairVisualRenderingDefaultsViewModel(RuntimeBasePairVisualRenderingOptions, _defaultBasePairVisualRenderingOptions);
                IUIVisualizer uiVisualizer = Resolve<IUIVisualizer>();
                if (uiVisualizer != null)
                {
                    bool? result = uiVisualizer.ShowDialog(typeof(DefaultBasePairRenderingOptionsEditor).ToString(), vm);
                    if (result != null && result.Value)
                    {
                        //We now need to copy the "potentially" modified runtime set
                        BasePairVisualRenderingOptionsDataCollection newRuntimeOptions = new BasePairVisualRenderingOptionsDataCollection();
                        foreach (var newRuntimeOptionSet in vm.RuntimeOptionsSets)
                            newRuntimeOptions.Add(newRuntimeOptionSet);
                        Settings.Default.BasePairVisualRenderingOptionSets = newRuntimeOptions;
                        Settings.Default.Save();
                        RuntimeBasePairVisualRenderingOptions = newRuntimeOptions;
                    }
                }
            }
        }

        private void OnEditDefaultNucleotideVisualProperties()
        {
            if (CurrentStructure != null)
            {
                NucleotideVisualRenderingDefaultsViewModel vm = new NucleotideVisualRenderingDefaultsViewModel(RuntimeNucleotideVisualRenderingOptions, _defaultNucleotideVisualRenderingOptions);
                IUIVisualizer uiVisualizer = Resolve<IUIVisualizer>();
                if (uiVisualizer != null)
                {
                    bool? result = uiVisualizer.ShowDialog(typeof(DefaultNucleotideRenderingOptionsEditor).ToString(), vm);
                    if (result != null && result.Value)
                    {
                        //We now want to copy the "potentially" modified runtime set.
                        //RuntimeNucleotideVisualRenderingOptions.Clear();
                        NucleotideVisualRenderingOptionsDataCollection newRuntimeOptions = new NucleotideVisualRenderingOptionsDataCollection();
                        foreach (var newRuntimeOptionSet in vm.RuntimeOptionsSets)
                            newRuntimeOptions.Add(newRuntimeOptionSet);
                        Settings.Default.NucleotideVisualRenderingOptionSets = newRuntimeOptions;
                        Settings.Default.Save();
                        RuntimeNucleotideVisualRenderingOptions = newRuntimeOptions;
                    }
                }
            }
        }

        private void OnExportDiagramToXRNAFile()
        {
            if (CurrentStructure != null)
            {
                RaiseSynchronizeStructureModelRequest();
                string exportFileName = PromptForOutputFileName(".ss");
                //Proceed with the export
                if (exportFileName != null)
                {
                    try
                    {
                        ExportDiagramToXRNA.ExportDiagram(CurrentStructure, exportFileName);
                    }
                    catch (Exception e)
                    {
                        IErrorVisualizer errMesg = Resolve<IErrorVisualizer>();
                        errMesg.Show("Error",
                            string.Format("Error: Failed exporting current diagram to file {0}, details: \n{1}\n{2}", exportFileName, e.Message, e.StackTrace));
                        exportFileName = null;
                    }
                }
            }
        }

        private void OnExportDiagramToCTFile()
        {
            if (CurrentStructure != null)
            {
                string exportFileName = PromptForOutputFileName(".ct");
                //Proceed with the export
                if (exportFileName != null)
                {
                    try
                    {
                        ExportDiagramToCT.ExportDiagram(CurrentStructure, exportFileName);
                    }
                    catch (Exception e)
                    {
                        IErrorVisualizer errMesg = Resolve<IErrorVisualizer>();
                        errMesg.Show("Error",
                            string.Format("Error: Failed exporting current diagram to file {0}, details: \n{1}\n{2}", exportFileName, e.Message, e.StackTrace));
                        exportFileName = null;
                    }
                }
            }
        }

        private void OnExportDiagramToBPSeqFile()
        {
            if (CurrentStructure != null)
            {
                string exportFileName = PromptForOutputFileName(".bpseq");
                //Proceed with the export
                if (exportFileName != null)
                {
                    try
                    {
                        ExportDiagramToBPSeq.ExportDiagram(CurrentStructure, exportFileName);
                    }
                    catch (Exception e)
                    {
                        IErrorVisualizer errMesg = Resolve<IErrorVisualizer>();
                        errMesg.Show("Error",
                            string.Format("Error: Failed exporting current diagram to file {0}, details: \n{1}\n{2}", exportFileName, e.Message, e.StackTrace));
                        exportFileName = null;
                    }
                }
            }
        }

        private void OnExportDiagramToRNAmlFile()
        {
            if (CurrentStructure != null)
            {
                RaiseSynchronizeStructureModelRequest();
                string exportFileName = PromptForOutputFileName(".rnaml");
                //Proceed with the export
                if (exportFileName != null)
                {
                    try
                    {
                        ExportDiagramToRNAml.ExportDiagram(CurrentStructure, exportFileName);
                    }
                    catch (Exception e)
                    {
                        IErrorVisualizer errMesg = Resolve<IErrorVisualizer>();
                        errMesg.Show("Error",
                            string.Format("Error: Failed exporting current diagram to file {0}, details: \n{1}\n{2}", exportFileName, e.Message, e.StackTrace));
                        exportFileName = null;
                    }
                }
            }
        }

        private void OnExportDiagramToStrmlFile()
        {
            if (CurrentStructure != null)
            {
                RaiseSynchronizeStructureModelRequest();
                string exportFileName = PromptForOutputFileName(".strml");
                //Proceed with the export
                if (exportFileName != null)
                {
                    try
                    {
                        ExportDiagramToStrml.ExportDiagram(CurrentStructure, exportFileName);
                    }
                    catch(Exception e)
                    {
                        IErrorVisualizer errMesg = Resolve<IErrorVisualizer>();
                        errMesg.Show("Error",
                            string.Format("Error: Failed exporting current diagram to file {0}, details: \n{1}\n{2}", exportFileName, e.Message, e.StackTrace));
                        exportFileName = null;
                    }
                }
            }
        }

        private void OnClearInMemoryModels()
        {
            if (InMemoryModelCount > 0)
            {
                List<string> keysForInMemoryModels = _loadedStructures.Where(kvp => kvp.Value != null && kvp.Value.InMemoryDataOnly).Select(kvp => kvp.Key).ToList<string>();
                foreach (var inMemoryModelKey in keysForInMemoryModels)
                {
                    _loadedStructures.Remove(inMemoryModelKey);
                }
                OnPropertyChanged("LoadedModelCount", "InMemoryModelCount");
            }
        }

        private void OnSwitchStructureModel()
        {
            if (LoadedModelCount > 0)
            {
                SecondaryStructureModelSelectionViewModel sssvm = new SecondaryStructureModelSelectionViewModel(_loadedStructures.Values, CurrentStructure);
                IUIVisualizer uiVisualizer = Resolve<IUIVisualizer>();
                if (uiVisualizer != null)
                {
                    bool? result = uiVisualizer.ShowDialog(typeof(SecondaryStructureEntitySelector).ToString(), sssvm);
                    if (result != null && result.Value)
                    {
                        if (sssvm.SelectedEntity == CurrentStructure) //No Change
                            return;

                        IMessageVisualizer messagebox = Resolve<IMessageVisualizer>();
                        if (messagebox != null && (IsDirty() || CurrentDrawingDirtyState))
                        {
                            MessageResult messageresult = messagebox.Show("Warning", "Warning: Changes to the current drawing detected. This operation will discard those changes, please consider exporting the current drawing before proceeding. Click Yes to stop this operation or No to proceeed", MessageButtons.YesNo);
                            if (messageresult == MessageResult.Yes)
                                return;
                        }

                        InterruptEditTracking();
                        CurrentStructure = sssvm.SelectedEntity;
                        OnPropertyChanged("IsStructureModelSetLinkable");
                        RaiseReloadDrawingRequest();
                        UninterruptEditTracking();
                    }
                }
            }
        }

        private void OnCreateNewDiagramByTemplate()
        {
            if (IsStructureModelSetLinked)
            {
                IBioSequence targetSequenceForDiagramGeneration = null;
                //We need to filter the set of sequences from the linked alignment view for which structure model renderings are not available in "this" view.
                var elegibleseqs = from seqentity in _linkedAlignment.Rows
                                   from seqidnorendering in _linkedAlignment.Rows.Select(seq => seq.Id).Except(_loadedStructures.Values.Select(smd => smd.SequenceId))
                                   where seqentity.Id == seqidnorendering
                                   select seqentity;
                TemplateDiagramTargetSequenceSelectionViewModel tdtssvm = new TemplateDiagramTargetSequenceSelectionViewModel(elegibleseqs, CurrentStructure);
                IUIVisualizer uiVisualizer = Resolve<IUIVisualizer>();
                if (uiVisualizer != null)
                {
                    bool? result = uiVisualizer.ShowDialog(typeof(TemplateDiagramTargetSequenceSelector).ToString(), tdtssvm);
                    if (result != null && result.Value)
                    {
                        targetSequenceForDiagramGeneration = tdtssvm.SelectedEntity;
                    }
                }

                if (targetSequenceForDiagramGeneration != null)
                {
                    IBioSequence alignedSeqForCurrentSM = _linkedAlignment.Rows.Where(seq => seq.Id == CurrentStructure.SequenceId).FirstOrDefault();
                    if (alignedSeqForCurrentSM != null)
                    {
                        try
                        {
                            //Ok, now we can call the templating algorithm, this is "probably" fast enought to do on the UI thread, but for multiples, we need this to be async.
                            StructureModelData templateStructureModel = TemplateDiagram.CreateDiagramForSequenceByTemplate(CurrentStructure, alignedSeqForCurrentSM, targetSequenceForDiagramGeneration);
                            if (templateStructureModel != null)
                            {
                                templateStructureModel.InMemoryDataOnly = true;
                                templateStructureModel.Title = string.Format("Structure Editor: Templated Diagram, Scientific Name: {0}", templateStructureModel.Entity.Sequence.ScientificName);
                                templateStructureModel.Key = string.Format("{0}::{1}::{2}", templateStructureModel.Entity.Sequence.Id, templateStructureModel.Entity.Sequence.SourceId, templateStructureModel.Entity.Sequence.ScientificName);
                                _loadedStructures.Add(templateStructureModel.Key, templateStructureModel);
                                if(_commLinkProxy != null)
                                    _commLinkProxy.FireStructureModelsAvailableChangedEvent();
                                OnPropertyChanged("LoadedModelCount", "InMemoryModelCount");

                                IMessageVisualizer messageVisualizer = Resolve<IMessageVisualizer>();
                                if (messageVisualizer != null)
                                {
                                    messageVisualizer.Show("Secondary Structure Editor Message", string.Format("Created a new diagram for (SeqID: {0}, {1}) successfully.", targetSequenceForDiagramGeneration.Id, targetSequenceForDiagramGeneration.ScientificName), MessageButtons.OK);
                                }
                            }
                        }
                        catch (Exception e)
                        {
                            IErrorVisualizer errorVisualizer = Resolve<IErrorVisualizer>();
                            if (errorVisualizer != null)
                            {
                                errorVisualizer.Show("Error", string.Format("Error: Failed Creating Diagram by Template, details: \n{0}\n{1}", e.Message, e.StackTrace));
                            }
                        }
                    }
                }
            }
        }

        private string PromptForOutputFileName(string defaultExt)
        {
            Debug.Assert(defaultExt != null);
            string exportFileName = null;
            OpenFileDialog dialog = new OpenFileDialog()
            {
                DefaultExt = defaultExt,
                Multiselect = false,
                Title = "Select an Output File",
                CheckFileExists = false,
                CheckPathExists = true,
                AddExtension = true
            };
            if (dialog.ShowDialog().Value)
            {
                exportFileName = dialog.FileName;
            }

            if (exportFileName != null)
            {
                IMessageVisualizer mesg = Resolve<IMessageVisualizer>();
                if (File.Exists(exportFileName))
                {
                    MessageResult result = mesg.Show("Warning",
                        string.Format("Warning: Output file: {0} already exists, do you want to overwrite the existing file?", exportFileName),
                        MessageButtons.YesNo);
                    if (result == MessageResult.OK)
                    {
                        try
                        {
                            File.Delete(exportFileName);
                        }
                        catch
                        {
                            IErrorVisualizer errMesg = Resolve<IErrorVisualizer>();
                            errMesg.Show("Error",
                                string.Format("Error: Cannot overwrite target output file: {0}", exportFileName));
                            exportFileName = null;
                        }
                    }
                }
                else
                {
                    try
                    {
                        FileStream testFile = File.Create(exportFileName);
                        testFile.Close();
                        File.Delete(exportFileName);
                    }
                    catch
                    {
                        IErrorVisualizer errMesg = Resolve<IErrorVisualizer>();
                        errMesg.Show("Error",
                            string.Format("Error: Target output file {0} is not valid or user lacks permissions to write in the selected location.", exportFileName));
                        exportFileName = null;
                    }
                }
            }
            return exportFileName;
        }

        private void ReloadRuntimeNucleotideVisualRenderingOptions()
        {
            _defaultNucleotideVisualRenderingOptions = new NucleotideVisualRenderingOptionsDataCollection();
            _defaultNucleotideVisualRenderingOptions.Add(NucleotideVisualRenderingOptionsData.CreateDefault(Settings.Default.DefaultNucleotideVisualRenderingOptionSetLabel));
            
            NucleotideVisualRenderingOptionsDataCollection runtimeOptionsSets = new NucleotideVisualRenderingOptionsDataCollection();
            if (Settings.Default.NucleotideVisualRenderingOptionSets != null && Settings.Default.NucleotideVisualRenderingOptionSets.Count() > 0)
            {
                foreach (var ntOptionsDataSet in Settings.Default.NucleotideVisualRenderingOptionSets) //We load the actual per-user configured runtime defaults.
                    runtimeOptionsSets.Add(new NucleotideVisualRenderingOptionsData(ntOptionsDataSet));
            }
            else //We copy the built-in defaults into the per-user defaults
            {
                foreach (var builtinDefaultNtOptionsDataSet in _defaultNucleotideVisualRenderingOptions)
                    runtimeOptionsSets.Add(new NucleotideVisualRenderingOptionsData(builtinDefaultNtOptionsDataSet));
                Settings.Default.NucleotideVisualRenderingOptionSets = runtimeOptionsSets;
                Settings.Default.Save();
            }
            RuntimeNucleotideVisualRenderingOptions = runtimeOptionsSets;
        }

        private void ReloadRuntimeBasePairVisualRenderingOptions()
        {
            _defaultBasePairVisualRenderingOptions = new BasePairVisualRenderingOptionsDataCollection();
            _defaultBasePairVisualRenderingOptions.Add(BasePairVisualRenderingOptionsData.CreateDefault(Settings.Default.DefaultBasePairVisualRenderingOptionSetLabel));
            
            BasePairVisualRenderingOptionsDataCollection runtimeOptionsSets = new BasePairVisualRenderingOptionsDataCollection();
            if (Settings.Default.BasePairVisualRenderingOptionSets != null && Settings.Default.BasePairVisualRenderingOptionSets.Count() > 0)
            {
                foreach (var bpOptionsDataSet in Settings.Default.BasePairVisualRenderingOptionSets) //We load the actual per-user configured runtime defaults.
                    runtimeOptionsSets.Add(new BasePairVisualRenderingOptionsData(bpOptionsDataSet));
            }
            else //We copy the built-in defaults into the per-user defaults
            {
                foreach (var builtinDefaultBpOptionsDataSet in _defaultBasePairVisualRenderingOptions)
                    runtimeOptionsSets.Add(new BasePairVisualRenderingOptionsData(builtinDefaultBpOptionsDataSet));
                Settings.Default.BasePairVisualRenderingOptionSets = runtimeOptionsSets;
                Settings.Default.Save();
            }
            RuntimeBasePairVisualRenderingOptions = runtimeOptionsSets;
        }

        private SecondaryStructureEditorBirdsEyeViewModel _beView;
        internal SecondaryStructureEditorBirdsEyeViewModel BirdsEyeViewModel
        {
            get
            {
                if (_beView == null)
                {
                    _beView = new SecondaryStructureEditorBirdsEyeViewModel(this);
                    _beView.ViewClosed += v => { _beView.Dispose(); _beView = null; IsBirdsEyeViewShowing = false; };
                }
                return _beView;
            }
        }

        private SecondaryStructureEditorVisualComponentInspectorViewModel _propertyInspectorView;
        internal SecondaryStructureEditorVisualComponentInspectorViewModel PropertyInspectorViewModel
        {
            get
            {
                if (_propertyInspectorView == null)
                {
                    _propertyInspectorView = new SecondaryStructureEditorVisualComponentInspectorViewModel(this);
                    _propertyInspectorView.ViewClosed += v => { _propertyInspectorView.Dispose(); _propertyInspectorView = null; IsPropertyInspectorViewShowing = false; };
                }
                else { _propertyInspectorView.SynchronizeToSecondaryStructureEditorState(); }
                return _propertyInspectorView;
            }
        }

        private MenuItem _translateRangeMenu;
        private MenuItem TranslateRangeMenu
        {
            get
            {
                if (_translateRangeMenu == null)
                {
                    _translateRangeMenu = new MenuItem("Translate");
                    _translateRangeMenu.Children.Add(new MenuItem("Left") { Command = TranslateStructureElementLeftCommand, GestureText = "Ctrl+J" });
                    _translateRangeMenu.Children.Add(new MenuItem("Right") { Command = TranslateStructureElementRightCommand, GestureText = "Ctrl+L" });
                    _translateRangeMenu.Children.Add(new MenuItem("Up") { Command = TranslateStructureElementUpCommand, GestureText = "Ctrl+I" });
                    _translateRangeMenu.Children.Add(new MenuItem("Down") { Command = TranslateStructureElementDownCommand, GestureText = "Ctrl+M" });
                    _translateRangeMenu.Children.Add(new MenuItem());
                    _translateRangeMenu.Children.Add(new MenuItem("By Vector...") { Command = TranslateStructureElementByCommand });
                }
                return _translateRangeMenu;
            }
        }

        private MenuItem _translateStructureElementMenu;
        private MenuItem TranslateStructureElementMenu
        {
            get
            {
                if (_translateStructureElementMenu == null)
                {
                    _translateStructureElementMenu = new MenuItem("Translate");
                    _translateStructureElementMenu.Children.Add(new MenuItem("Left") { Command = TranslateStructureElementLeftCommand, GestureText = "Ctrl+J" });
                    _translateStructureElementMenu.Children.Add(new MenuItem("Right") { Command = TranslateStructureElementRightCommand, GestureText = "Ctrl+L" });
                    _translateStructureElementMenu.Children.Add(new MenuItem("Up") { Command = TranslateStructureElementUpCommand, GestureText = "Ctrl+I" });
                    _translateStructureElementMenu.Children.Add(new MenuItem("Down") { Command = TranslateStructureElementDownCommand, GestureText = "Ctrl+M" });
                    _translateStructureElementMenu.Children.Add(new MenuItem());
                    _translateStructureElementMenu.Children.Add(new MenuItem("To Location...") { Command = TranslateStructureElementToCommand });
                    _translateStructureElementMenu.Children.Add(new MenuItem("By Vector...") { Command = TranslateStructureElementByCommand });
                }
                return _translateStructureElementMenu;
            }
        }

        private MenuItem _rotateStructureElementMenu;
        private MenuItem RotateStructureElementMenu
        {
            get
            {
                if (_rotateStructureElementMenu == null)
                {
                    _rotateStructureElementMenu = new MenuItem("Rotate");
                    _rotateStructureElementMenu.Children.Add(new MenuItem("Right") { Command = RotateStructureElementRightIncrementCommand, GestureText = "-" });
                    _rotateStructureElementMenu.Children.Add(new MenuItem("Left") { Command = RotateStructureElementLeftIncrementCommand, GestureText = "+" });
                    _rotateStructureElementMenu.Children.Add(new MenuItem());
                    _rotateStructureElementMenu.Children.Add(new MenuItem("Right 90°") { Command = RotateStructureElementRightCommand, GestureText = "Alt+L" });
                    _rotateStructureElementMenu.Children.Add(new MenuItem("Left 90°") { Command = RotateStructureElementLeftCommand, GestureText = "Alt+J" });
                    _rotateStructureElementMenu.Children.Add(new MenuItem());
                    _rotateStructureElementMenu.Children.Add(new MenuItem("To Angle...") { Command = RotateStructureElementToCommand });
                    _rotateStructureElementMenu.Children.Add(new MenuItem("By Angle...") { Command = RotateStructureElementByCommand });
                }
                return _rotateStructureElementMenu;
            }
        }

        private MenuItem _resizeBasePairMenu;
        private MenuItem ResizeBasePairMenu
        {
            get
            {
                if (_resizeBasePairMenu == null)
                {
                    _resizeBasePairMenu = new MenuItem("Resize");
                    _resizeBasePairMenu.Children.Add(new MenuItem("Selected") { Command = ResizeStructureElementCommand });
                    _resizeBasePairMenu.Children.Add(new MenuItem("All") { Command = ResizeAllBasePairsCommand });

                }
                return _resizeBasePairMenu;
            }
        }

        private MenuItem _resizeHelixMenu;
        private MenuItem ResizeHelixMenu
        {
            get
            {
                if (_resizeHelixMenu == null)
                {
                    _resizeHelixMenu = new MenuItem("Resize");
                    _resizeHelixMenu.Children.Add(new MenuItem("Selected") { Command = ResizeStructureElementCommand });
                    _resizeHelixMenu.Children.Add(new MenuItem("All") { Command = ResizeAllHelicesCommand });

                }
                return _resizeHelixMenu;
            }
        }

        private MenuItem _resizeLoopMenu;
        private MenuItem ResizeLoopMenu
        {
            get
            {
                if (_resizeLoopMenu == null)
                {
                    _resizeLoopMenu = new MenuItem("Resize");
                    _resizeLoopMenu.Children.Add(new MenuItem("Selected") { Command = ResizeStructureElementCommand });
                }
                return _resizeLoopMenu;
            }
        }

        private SecondaryStructureDrawingPropertyContext PropertyContextManager { get; set; }
        private bool PauseEditTracking { get; set; }
        private bool PauseEditNotification { get; set; }

        private void RaiseSelectStructureElementContainingNucleotideIndexRequest(int nucleotideIndex)
        {
            if (SelectStructureElementContainingNucleotide != null)
                SelectStructureElementContainingNucleotide(this, new SelectStructureElementContainingNucleotideIndexEventArgs(nucleotideIndex));
        }

        private void RaiseReloadDrawingRequest()
        {
            if (StructureModelDataChanged != null)
                StructureModelDataChanged(this, EventArgs.Empty);
        }

        private void RaiseSynchronizeStructureModelRequest()
        {
            if (SynchronizeStructureModelState != null)
                SynchronizeStructureModelState(this, EventArgs.Empty);
        }

        private void RaiseUndoLastEditRequest()
        {
            if (UndoLastEdit != null)
                UndoLastEdit(this, EventArgs.Empty);
        }

        private void RaiseStructureDrawingVisualEdited()
        {
            if (NotifyStructureDrawingVisualEdited != null && !PauseEditNotification)
                NotifyStructureDrawingVisualEdited(this, EventArgs.Empty);
        }

        private void RaiseDeleteSelectedBasePairRequest()
        {
            if (DeleteSelectedBasePair != null)
                DeleteSelectedBasePair(this, EventArgs.Empty);
        }

        private void RaiseDeleteSelectedHelixRequest()
        {
            if (DeleteSelectedHelix != null)
                DeleteSelectedHelix(this, EventArgs.Empty);
        }

        private void RaiseDeleteBasePairsFromRangeRequest()
        {
            if (DeleteBasePairsFromRange != null)
                DeleteBasePairsFromRange(this, EventArgs.Empty);
        }

        private void RaiseCreateHelixRequest(int fivePrimeStart, int threePrimeEnd, int fivePrimeEnd)
        {
            if (CreateStructureElement != null)
                CreateStructureElement(this, new CreateStructureElementEventArgs(fivePrimeStart, threePrimeEnd, fivePrimeEnd));
        }

        private void RaiseCreateBasePairRequest(int fivePrime, int threePrime)
        {
            if (CreateStructureElement != null)
                CreateStructureElement(this, new CreateStructureElementEventArgs(fivePrime, threePrime));
        }

        private void RaiseSnapSelectedHelixToOpeningBasePairRequest()
        {
            if (SnapSelectedHelixToOpeningBasePair != null)
                SnapSelectedHelixToOpeningBasePair(this, EventArgs.Empty);
        }

        private void RaiseSnapSelectedHelixToClosingBasePairRequest()
        {
            if (SnapSelectedHelixToClosingBasePair != null)
                SnapSelectedHelixToClosingBasePair(this, EventArgs.Empty);
        }

        private void RaiseResizeAllBasePairsRequest(double width)
        {
            if (ResizeStructureElement != null)
            {
                ResizeStructureElementEventArgs args = new ResizeStructureElementEventArgs(ExecutionMode.All, StructureElementSelectionMode.BasePair);
                args.SetWidth(width);
                ResizeStructureElement(this, args);
            }
        }

        private void RaiseResizeAllHelicesRequest(bool setPadding, bool setWidth, double padding, double width)
        {
            if (ResizeStructureElement != null)
            {
                ResizeStructureElementEventArgs args = new ResizeStructureElementEventArgs(ExecutionMode.All, StructureElementSelectionMode.Helix);
                if (setPadding)
                    args.SetPadding(padding);

                if (setWidth)
                    args.SetWidth(width);
                ResizeStructureElement(this, args);
            }
        }

        private void RaiseResizeSelectedStructureElementRequest(bool setPadding, bool setWidth, double padding, double width)
        {
            if (ResizeStructureElement != null)
            {
                ResizeStructureElementEventArgs args = new ResizeStructureElementEventArgs();
                if (setPadding)
                    args.SetPadding(padding);

                if (setWidth)
                    args.SetWidth(width);
                ResizeStructureElement(this, args);
            }    
        }

        private void RaiseTranslateSelectedStructureElementRequest(Point translation, TranslationType mode)
        {
            if (TranslateSelectedStructureElement != null)
                TranslateSelectedStructureElement(this, new TranslateSelectedStructureElementEventArgs(translation, mode));
        }

        private void RaiseRotateSelectedStructureElementRequest(double angleInDegrees, RotationType mode)
        {
            if (RotateSelectedStructureElement != null)
                RotateSelectedStructureElement(this, new RotateSelectedStructureElementEventArgs(angleInDegrees, mode));
        }

        private void RaiseEditSelectedStructureElementPropertiesRequest(StructureElementPropertyChangeSet changeSet)
        {
            if (EditStructureElementProperties != null)
                EditStructureElementProperties(this, new EditStructureElementPropertiesEventArgs(changeSet));
        }

        private void RaiseEditSelectedRangeStructureElementPropertiesRequest(StructureElementPropertyChangeSet changeSet, List<IBioSymbol> filter)
        {
            if (EditStructureElementProperties != null)
                EditStructureElementProperties(this, new EditStructureElementPropertiesEventArgs(ExecutionMode.FilteredRange, changeSet, filter));
        }

        private void RaiseEditNucleotideElementPropertiesRequest(StructureElementPropertyChangeSet changeSet, List<IBioSymbol> filter)
        {
            if (EditStructureElementProperties != null)
            {
                if (filter != null && filter.Count > 0)
                {
                    EditStructureElementProperties(this, new EditStructureElementPropertiesEventArgs(ExecutionMode.Filtered, changeSet, filter));
                }
                else
                {
                    EditStructureElementProperties(this, new EditStructureElementPropertiesEventArgs(StructureElementSelectionMode.Nucleotide, changeSet));
                }
            }
        }

        private void RaiseEditBasePairElementPropertiesRequest(StructureElementPropertyChangeSet changeSet, List<string> filter)
        {
            if (EditStructureElementProperties != null)
            {
                if (filter != null && filter.Count > 0)
                {
                    EditStructureElementProperties(this, new EditStructureElementPropertiesEventArgs(changeSet, filter));
                }
                else
                {
                    EditStructureElementProperties(this, new EditStructureElementPropertiesEventArgs(StructureElementSelectionMode.BasePair, changeSet));
                }
            }
        }
    }
}
